Skip to content

feat: add MTV operator upgrade/update migration testing#487

Merged
myakove merged 16 commits into
RedHatQE:mainfrom
krcmarik:upgrade
May 28, 2026
Merged

feat: add MTV operator upgrade/update migration testing#487
myakove merged 16 commits into
RedHatQE:mainfrom
krcmarik:upgrade

Conversation

@krcmarik
Copy link
Copy Markdown
Contributor

@krcmarik krcmarik commented May 14, 2026

Add upgrade test infrastructure:

  • test_upgrade_migration.py: incremental test class that creates migration resources, upgrades MTV operator, verifies post-upgrade state, and runs cold migration
  • utilities/upgrade.py: subprocess-based upgrade runner with fd 3 support for mtv-autodeploy logging, plus pod readiness and plan verification
  • Add @pytest.mark.upgrade to warm migration for post-upgrade warm test
  • Remove tier0 from cold migration (covered by upgrade test)
  • Add upgrade_script_path, mtv_upgrade_to_version, mtv_upgrade_to_source, mtv_upgrade_image_index config parameters
  • Add 'upgrade' marker to pytest.ini
  • Add MtvUpgradeError and ForkliftPodsNotRunningError exceptions

Summary by CodeRabbit

  • New Features

    • End-to-end upgrade testing that automates operator upgrade and cold-migration validation.
  • Tests

    • Added a dedicated upgrade test suite and a new "upgrade" pytest marker; applied marker to existing warm-migration sanity tests.
    • New fixtures to prepare pre-upgrade resources and provide the upgrade script path.
  • Chores

    • Configurable upgrade parameters, an upgrade runner utility, a specific upgrade error type, added git runtime support, and test docs/usage examples.

Review Change Stack

Add upgrade test infrastructure:
- test_upgrade_migration.py: incremental test class that creates migration
  resources, upgrades MTV operator, verifies post-upgrade state, and runs
  cold migration
- utilities/upgrade.py: subprocess-based upgrade runner with fd 3 support
  for mtv-autodeploy logging, plus pod readiness and plan verification
- Add @pytest.mark.upgrade to warm migration for post-upgrade warm test
- Remove tier0 from cold migration (covered by upgrade test)
- Add upgrade_script_path, mtv_upgrade_to_version, mtv_upgrade_to_source,
  mtv_upgrade_image_index config parameters
- Add 'upgrade' marker to pytest.ini
- Add MtvUpgradeError and ForkliftPodsNotRunningError exceptions
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4b1f6542-2a39-4a2c-a8d0-0f61662c7f3a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds MTV operator upgrade testing: new MtvUpgradeError, an upgrade pytest marker, upgrade config and test params, a runner run_mtv_upgrade(), conftest fixtures to resolve the upgrade script and build maps/Plan, and a parameterized end-to-end TestUpgradeColdMigration.

Changes

MTV Operator Upgrade Test Support

Layer / File(s) Summary
Exception, marker, configuration setup
exceptions/exceptions.py, pytest.ini, tests/tests_config/config.py, pyproject.toml, Dockerfile, README.md
New MtvUpgradeError exception; added upgrade pytest marker; module-level config variables upgrade_repo_url, upgrade_repo_ref, upgrade_script_path, mtv_upgrade_to_version, mtv_upgrade_to_source, mtv_upgrade_image_index; tests_params["test_upgrade_cold_migration"]; added gitpython runtime dependency and runtime git package; README marker and Upgrade Tests docs added.
Conftest fixtures (script + maps + plan)
tests/upgrade/conftest.py
Session-scoped upgrade_script_path fixture that validates pytest_testconfig, shallow-clones repo@ref, ensures script exists and is executable, and returns its path; class-scoped pre_upgrade_storage_map, pre_upgrade_network_map, and pre_upgrade_plan_resource fixtures that build StorageMap/NetworkMap and create a Plan CR with populated VM IDs.
Upgrade runner
utilities/upgrade.py
Adds run_mtv_upgrade(script_path, mtv_version, mtv_source, image_index="") which builds subprocess env (MTV_VERSION, MTV_SOURCE uppercased, IMAGE_INDEX, CLUSTER_* creds), runs the external upgrade script with a 3600s timeout from the script directory, masks sensitive output, and raises MtvUpgradeError on timeout or non-zero exit.
Upgrade + migration test class
tests/upgrade/test_upgrade_migration.py
TestUpgradeColdMigration parameterized pytest class that invokes run_mtv_upgrade, verifies post-upgrade MTV version and Plan readiness, executes migration, and validates migrated VMs via check_vms.
Marker integration with warm tests
tests/warm/test_mtv_warm_migration.py
Adds @pytest.mark.upgrade decorator to TestSanityWarmMtvMigration for test categorization and selective runs.

Sequence Diagram(s)

sequenceDiagram
  participant Test as PyTest
  participant Fixture as upgrade_script_path
  participant Runner as run_mtv_upgrade
  participant UpgradeScript as ExternalScript
  participant MTV as MTV_Operator
  participant OCP as OCP_API

  Test->>Fixture: request upgrade script path (repo_url, repo_ref, script_path)
  Fixture->>UpgradeScript: clone repo and resolve script path
  Test->>Runner: call(script_path, mtv_version, mtv_source, image_index)
  Runner->>UpgradeScript: execute script (env includes MTV_VERSION, MTV_SOURCE, IMAGE_INDEX, CLUSTER_*)
  UpgradeScript-->>Runner: exit code or timeout (stdout/stderr)
  Runner->>MTV: operator upgraded (applied by script)
  MTV->>OCP: report new version
  Test->>OCP: verify MTV version and Plan READY=true
  Test->>OCP: execute migration and run check_vms
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

verified, size/L, commented-coderabbitai[bot]

Suggested reviewers

  • myakove
  • solenoci
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding MTV operator upgrade/update migration testing capabilities to the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@redhat-qe-bot
Copy link
Copy Markdown

Report bugs in Issues

Welcome! 🎉

This pull request will be automatically processed with the following features:

🔄 Automatic Actions

  • Reviewer Assignment: Reviewers are automatically assigned based on the OWNERS file in the repository root
  • Size Labeling: PR size labels (XS, S, M, L, XL, XXL) are automatically applied based on changes
  • Issue Creation: Disabled for this repository
  • Branch Labeling: Branch-specific labels are applied to track the target branch
  • Auto-verification: Auto-verified users have their PRs automatically marked as verified
  • Labels: All label categories are enabled (default configuration)

📋 Available Commands

PR Status Management

  • /wip - Mark PR as work in progress (adds WIP: prefix to title)
  • /wip cancel - Remove work in progress status
  • /hold - Block PR merging (approvers only)
  • /hold cancel - Unblock PR merging
  • /verified - Mark PR as verified
  • /verified cancel - Remove verification status
  • /reprocess - Trigger complete PR workflow reprocessing (useful if webhook failed or configuration changed)
  • /regenerate-welcome - Regenerate this welcome message

Review & Approval

  • /lgtm - Approve changes (looks good to me)
  • /approve - Approve PR (approvers only)
  • /automerge - Enable automatic merging when all requirements are met (maintainers and approvers only)
  • /assign-reviewers - Assign reviewers based on OWNERS file
  • /assign-reviewer @username - Assign specific reviewer
  • /check-can-merge - Check if PR meets merge requirements

Testing & Validation

  • /retest tox - Run Python test suite with tox
  • /retest build-container - Rebuild and test container image
  • /retest conventional-title - Validate commit message format
  • /retest all - Run all available tests

Container Operations

  • /build-and-push-container - Build and push container image (tagged with PR number)
    • Supports additional build arguments: /build-and-push-container --build-arg KEY=value

Cherry-pick Operations

  • /cherry-pick <branch> - Schedule cherry-pick to target branch when PR is merged
    • Multiple branches: /cherry-pick branch1 branch2 branch3

Label Management

  • /<label-name> - Add a label to the PR
  • /<label-name> cancel - Remove a label from the PR

✅ Merge Requirements

This PR will be automatically approved when the following conditions are met:

  1. Approval: /approve from at least one approver
  2. Status Checks: All required status checks must pass
  3. No Blockers: No wip, hold, has-conflicts labels and PR must be mergeable (no conflicts)
  4. Verified: PR must be marked as verified

📊 Review Process

Approvers and Reviewers

Approvers:

  • myakove
  • solenoci

Reviewers:

  • krcmarik
  • myakove
  • solenoci
Available Labels
  • hold
  • verified
  • wip
  • lgtm
  • approve
  • automerge
AI Features
  • Conventional Title: Mode: fix (claude/claude-opus-4-6[1m])
  • Cherry-Pick Conflict Resolution: Enabled (claude/claude-opus-4-6[1m])
  • Test Oracle: Triggers: approved (cursor/gpt-5.4-xhigh-fast); /test-oracle can be used anytime

💡 Tips

  • WIP Status: Use /wip when your PR is not ready for review
  • Verification: The verified label is removed on new commits unless the push is detected as a clean rebase
  • Cherry-picking: Cherry-pick labels are processed when the PR is merged
  • Container Builds: Container images are automatically tagged with the PR number
  • Permission Levels: Some commands require approver permissions
  • Auto-verified Users: Certain users have automatic verification and merge privileges

For more information, please refer to the project documentation or contact the maintainers.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/upgrade/test_upgrade_migration.py`:
- Line 203: Remove the unused fixture parameter target_namespace from the
test_check_vms signature because it is not referenced inside the test nor passed
to check_vms(); edit the test function definition (test_check_vms) to only
include the actual fixtures it uses and update any calls or references
accordingly so the test requests only necessary fixtures.

In `@utilities/upgrade.py`:
- Line 33: The Generator type annotation on function _duplicate_stdout_to_fd3 is
incomplete; change its return type from Generator[None] to Generator[None, None,
None] so the full type parameters (YieldType, SendType, ReturnType) are
specified for proper static analysis and IDE support.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fef25392-bc9f-4341-91f6-21262fdfb22b

📥 Commits

Reviewing files that changed from the base of the PR and between 4bcd4fb and 4e920c4.

📒 Files selected for processing (6)
  • exceptions/exceptions.py
  • pytest.ini
  • tests/tests_config/config.py
  • tests/upgrade/test_upgrade_migration.py
  • tests/warm/test_mtv_warm_migration.py
  • utilities/upgrade.py

Comment thread tests/upgrade/test_upgrade_migration.py Outdated
Comment thread utilities/upgrade.py Outdated
@krcmarik
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@utilities/upgrade.py`:
- Around line 42-94: Add a pre-flight validation in run_mtv_upgrade to verify
the provided script_path exists and is a file (and optionally executable) before
calling subprocess.run; if not, raise a clear exception (e.g., FileNotFoundError
or ValueError) with a message like "script_path not found: <path>" so the
failure is immediate and informative; place this check after the existing
empty-parameter guards and before constructing env/calling
_duplicate_stdout_to_fd3()/subprocess.run to ensure the function fails fast when
script_path is invalid.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3b502be4-26e8-404a-a214-6f8a305403c9

📥 Commits

Reviewing files that changed from the base of the PR and between 4e920c4 and d5ae56a.

📒 Files selected for processing (3)
  • tests/tests_config/config.py
  • tests/upgrade/test_upgrade_migration.py
  • utilities/upgrade.py

Comment thread utilities/upgrade.py Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes May 14, 2026
Comment thread tests/upgrade/conftest.py
Comment thread tests/upgrade/conftest.py
Comment thread tests/upgrade/test_upgrade_migration.py
Comment thread tests/upgrade/test_upgrade_migration.py
Comment thread utilities/upgrade.py
@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 25, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (3) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Missing upgrade config keys 🐞 Bug ☼ Reliability
Description
tests/upgrade/test_upgrade_migration.py directly indexes py_config for mtv_upgrade_* keys, which
raises KeyError when they are not defined. Since pytest.ini loads tests/tests_config/config.py as
the default --tc-file and it does not define these keys, running the upgrade suite can fail before
any upgrade logic executes.
Code

tests/upgrade/test_upgrade_migration.py[R54-61]

Evidence
The upgrade test uses direct py_config[...] indexing for mtv_upgrade_*, while pytest is
configured to load defaults from tests/tests_config/config.py, which does not define those keys—so
the access will raise KeyError unless users pass them explicitly.

tests/upgrade/test_upgrade_migration.py[54-61]
pytest.ini[4-15]
tests/tests_config/config.py[2-16]
tests/tests_config/config.py[628-644]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Upgrade tests index `py_config["mtv_upgrade_to_version"]`, `py_config["mtv_upgrade_to_source"]`, and `py_config["mtv_upgrade_image_index"]` directly. If these keys are not provided, pytest will fail with `KeyError` instead of a clear configuration error.

## Issue Context
`pytest.ini` configures pytest-testconfig to load `tests/tests_config/config.py` by default (`--tc-file=...`). That file currently does not define the new `mtv_upgrade_*` keys.

## Fix Focus Areas
- tests/upgrade/test_upgrade_migration.py[54-61]
- pytest.ini[4-15]
- tests/tests_config/config.py[2-16]
- tests/tests_config/config.py[628-644]

## Suggested fix
- Add default entries to `tests/tests_config/config.py` for:
 - `mtv_upgrade_to_version` (e.g., empty string)
 - `mtv_upgrade_to_source` (e.g., empty string)
 - `mtv_upgrade_image_index` (e.g., empty string)
 so they exist in `py_config`.
- In the upgrade test (or a session fixture), validate these values early using `py_config.get(...)` and raise `ValueError` with a helpful `--tc=...` message (similar to `upgrade_script_path` fixture) rather than relying on `KeyError`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. py_config.get() defaults for required 📘 Rule violation ≡ Correctness
Description
The new upgrade_script_path fixture reads required controlled config keys via `py_config.get(...,
"")`, which hides misconfiguration by supplying defaults. Controlled configuration must be accessed
without defaults (e.g., py_config["key"]) per policy.
Code

tests/upgrade/conftest.py[R45-47]

Evidence
The fixture uses py_config.get(..., "") for required controlled keys (upgrade_repo_url,
upgrade_repo_ref, upgrade_script_path), which is explicitly disallowed for configs we control
because it can mask missing configuration and changes failure modes.

CLAUDE.md: Config We Control Must Not Use Defaults; External Provider Data May Use .get() With Validation (Optional Flags Allowed)
tests/upgrade/conftest.py[45-47]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Required controlled configuration keys are accessed with `.get(..., "")`, which violates the rule that configs we control must not use defaults.

## Issue Context
`upgrade_repo_url`, `upgrade_repo_ref`, and `upgrade_script_path` are required inputs validated by this fixture and should fail fast via KeyError (or explicit validation) without introducing default values.

## Fix Focus Areas
- tests/upgrade/conftest.py[45-55]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Test methods lack Args sections 📘 Rule violation ⚙ Maintainability
Description
New test methods include docstrings but omit standard Args sections despite having parameters.
This reduces consistency and violates the required docstring structure for new functions.
Code

tests/upgrade/test_upgrade_migration.py[R54-118]

Evidence
The new test methods (e.g., test_upgrade_mtv, test_verify_post_upgrade, test_migrate_vms,
test_check_vms) have parameters but their docstrings do not include Args: sections, failing the
required standard docstring format for new functions.

CLAUDE.md: New Functions Must Use Standard Docstring Sections (Args/Returns/Raises) Appropriately
tests/upgrade/test_upgrade_migration.py[54-118]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New functions must use standard docstring sections. Several newly added test methods have parameters but only a one-line docstring without an `Args:` section.

## Issue Context
The rule applies to new functions; these test methods are new and have multiple injected fixtures/parameters.

## Fix Focus Areas
- tests/upgrade/test_upgrade_migration.py[54-118]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. README missing upgrade configuration 📘 Rule violation ⚙ Maintainability
Description
New upgrade tests introduce required --tc configuration keys and an upgrade marker, but
README.md does not document how to run/configure these upgrade tests. This can lead to
misconfiguration and confusion for users/CI when attempting to use the new upgrade workflow.
Code

tests/upgrade/conftest.py[R45-60]

Evidence
The new fixtures/tests require new --tc configuration keys for cloning/running the upgrade script
and add an upgrade pytest marker, which changes user-facing configuration and usage. README.md’s
test-running examples show --tc usage but do not document these new upgrade-specific keys/marker,
violating the requirement to update README when usage/config changes.

CLAUDE.md: Update README.md When Code Changes Affect Usage/Requirements/Installation/Configuration
tests/upgrade/conftest.py[45-60]
tests/upgrade/test_upgrade_migration.py[37-61]
pytest.ini[29-33]
README.md[634-690]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces new upgrade testing configuration keys and a new pytest marker, but README.md is not updated to reflect how to configure and run the new upgrade tests.

## Issue Context
Upgrade tests require new `--tc` parameters (e.g., `upgrade_repo_url`, `upgrade_repo_ref`, `upgrade_script_path`, `mtv_upgrade_to_version`, `mtv_upgrade_to_source`, `mtv_upgrade_image_index`) and introduce an `upgrade` marker.

## Fix Focus Areas
- README.md[634-690]
- tests/upgrade/conftest.py[45-60]
- tests/upgrade/test_upgrade_migration.py[37-61]
- pytest.ini[29-33]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Upgrade output leaks secrets ✓ Resolved 🐞 Bug ⛨ Security
Description
utilities/upgrade.py passes CLUSTER_PASSWORD via environment and then logs full stdout/stderr and
embeds them into raised exceptions unredacted. This can persist sensitive credentials in test logs
and failure reports whenever the upgrade script outputs them.
Code

utilities/upgrade.py[R37-74]

Evidence
The code explicitly injects CLUSTER_PASSWORD into the subprocess environment and then logs/raises
raw script output, so any credential-bearing output becomes part of persistent logs/exceptions.

utilities/upgrade.py[37-45]
utilities/upgrade.py[58-69]
utilities/upgrade.py[71-74]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`run_mtv_upgrade()` exports cluster credentials into the child process environment and logs the upgrade script output verbatim (and includes it in exception messages). This creates a direct path for credential exposure in CI logs and artifacts.

## Issue Context
The upgrade runner sets `CLUSTER_PASSWORD` in `env` and then logs `result.stdout`/`result.stderr`, and also includes `stdout`/`stderr` in the raised `MtvUpgradeError` message.

## Fix Focus Areas
- utilities/upgrade.py[37-45]
- utilities/upgrade.py[58-69]
- utilities/upgrade.py[71-74]

## Suggested fix
- Do not log full stdout/stderr at INFO/WARNING by default; either:
 - log at DEBUG level, or
 - log only a bounded tail (e.g., last N lines) on failure.
- Redact secrets before logging/raising (at minimum redact values of `CLUSTER_PASSWORD`, tokens, and any `oc login`-style command lines).
- Consider removing stdout/stderr from exception messages (or include only redacted/tailed output) so secrets don’t end up in stack traces and junit XML.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. Git ref cloning too strict 🐞 Bug ☼ Reliability
Description
tests/upgrade/conftest.py clones the upgrade repo using branch=repo_ref with depth=1, but the
config is named upgrade_repo_ref and described as a generic ref. This setup can fail for valid
refs that are not advertised as a remote branch/tag (or require more history), preventing the
upgrade fixture from locating the script.
Code

tests/upgrade/conftest.py[R45-60]

Evidence
The fixture reads upgrade_repo_ref as a generic ref string and passes it directly as the branch=
parameter while forcing a shallow clone, which constrains what refs can be used successfully.

tests/upgrade/conftest.py[45-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `upgrade_script_path` fixture treats `upgrade_repo_ref` as a git branch (`branch=repo_ref`) and forces a shallow clone (`depth=1`). This can break when users provide a ref that requires a fetch/checkout step beyond a shallow `--branch` clone.

## Issue Context
The fixture’s error message requests a generic `<ref>`, not specifically a branch name.

## Fix Focus Areas
- tests/upgrade/conftest.py[45-60]

## Suggested fix
- Clone without pinning `branch=` (or keep it optional), then explicitly checkout the requested ref:
 - `repo = Repo.clone_from(...)
    repo.git.checkout(repo_ref)`
- If you want shallow clones for speed, add a fallback strategy:
 - attempt shallow clone/checkout; on failure, retry with a non-shallow clone or `depth=None`.
- Optionally validate that `script_relative_path` is not absolute and does not escape the clone dir (prevent surprising execution paths).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. Unsafe decode of subprocess output 🐞 Bug ☼ Reliability
Description
utilities/upgrade.py decodes subprocess stdout/stderr using .decode() without specifying
encoding/error handling in both success and exception paths. Non-UTF8 bytes can raise
UnicodeDecodeError and mask the actual upgrade failure details.
Code

utilities/upgrade.py[R58-73]

Evidence
The upgrade runner decodes stdout/stderr multiple times without specifying encoding or errors=...,
which can raise decoding exceptions when output is not UTF-8.

utilities/upgrade.py[58-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`run_mtv_upgrade()` calls `.decode()` on raw subprocess output without `errors=...`. If output contains non-UTF8 bytes, decoding can raise `UnicodeDecodeError` and prevent the intended `MtvUpgradeError` from being raised with useful context.

## Issue Context
This affects both normal logging and the TimeoutExpired/CalledProcessError exception paths.

## Fix Focus Areas
- utilities/upgrade.py[58-73]

## Suggested fix
- Prefer running subprocess in text mode:
 - `subprocess.run(..., text=True, encoding="utf-8", errors="replace")`
 - then remove `.decode()` calls.
- If you keep bytes mode, use `.decode(errors="replace")` (or `backslashreplace`) consistently for stdout/stderr.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 25, 2026
Comment thread tests/upgrade/test_upgrade_migration.py
@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 25, 2026

Persistent review updated to latest commit 0cd8377

Comment thread tests/upgrade/test_upgrade_migration.py
Comment thread tests/upgrade/conftest.py
Redact sensitive fields (password, token, thumbprint) in upgrade
log output. Add git package to Dockerfile runtime stage for
upgrade test branch operations. Document upgrade test usage
in README.
@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 25, 2026

Persistent review updated to latest commit 3d6341b

1 similar comment
@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 25, 2026

Persistent review updated to latest commit 3d6341b

@krcmarik
Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
utilities/upgrade.py (2)

37-45: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

HIGH: Add explicit fail-fast validation for required inputs and config values.

mtv_source.upper() and env assembly assume non-None values; missing/None values will fail with indirect runtime errors instead of actionable messages. Validate script_path, mtv_version, mtv_source, and required py_config keys before building env and running subprocess.

Proposed fix
 def run_mtv_upgrade(
     script_path: str,
     mtv_version: str,
     mtv_source: str,
     image_index: str = "",
 ) -> None:
@@
+    if not script_path:
+        raise ValueError("script_path must be provided via --tc=upgrade_script_path:<path>")
+    if not mtv_version:
+        raise ValueError("mtv_version must be provided via --tc=mtv_upgrade_to_version:<version>")
+    if mtv_source is None or not mtv_source:
+        raise ValueError("mtv_source must be provided via --tc=mtv_upgrade_to_source:<source>")
+
+    cluster_username = py_config.get("cluster_username")
+    cluster_password = py_config.get("cluster_password")
+    cluster_host = py_config.get("cluster_host")
+    if cluster_username is None:
+        raise ValueError("cluster_username must be provided via --tc=cluster_username:<username>")
+    if cluster_password is None:
+        raise ValueError("cluster_password must be provided via --tc=cluster_password:<password>")
+    if cluster_host is None:
+        raise ValueError("cluster_host must be provided via --tc=cluster_host:<api-url>")
+
     env = os.environ.copy()
     env.update({
         "MTV_VERSION": mtv_version,
         "MTV_SOURCE": mtv_source.upper(),
         "IMAGE_INDEX": image_index,
-        "CLUSTER_USERNAME": py_config["cluster_username"],
-        "CLUSTER_PASSWORD": py_config["cluster_password"],
-        "CLUSTER_API_URL": py_config["cluster_host"],
+        "CLUSTER_USERNAME": cluster_username,
+        "CLUSTER_PASSWORD": cluster_password,
+        "CLUSTER_API_URL": cluster_host,
     })

As per coding guidelines: "Code must never result in None when None is not valid - fail early with clear errors using if value is None: checks".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utilities/upgrade.py` around lines 37 - 45, Validate inputs before building
env: explicitly check that script_path, mtv_version, mtv_source, and py_config
are not None (and that mtv_source is a string) and that py_config contains
required keys "cluster_username", "cluster_password", and "cluster_host"; if any
check fails raise a clear ValueError mentioning the missing/invalid field. Do
these checks in the same scope where env is constructed (the block that
references mtv_version, mtv_source, image_index and py_config) so you never call
mtv_source.upper() or access py_config[...] when values may be None or missing;
after validation you can safely build env as shown.

37-45: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

CRITICAL: Do not pass the full parent environment to an external upgrade script.

Using os.environ.copy() forwards all CI/runtime secrets to a script sourced from configurable repo/ref. Build a minimal allowlist environment to reduce credential leakage risk.

Proposed fix
-    env = os.environ.copy()
-    env.update({
+    env = {
         "MTV_VERSION": mtv_version,
         "MTV_SOURCE": mtv_source.upper(),
         "IMAGE_INDEX": image_index,
-        "CLUSTER_USERNAME": py_config["cluster_username"],
-        "CLUSTER_PASSWORD": py_config["cluster_password"],
-        "CLUSTER_API_URL": py_config["cluster_host"],
-    })
+        "CLUSTER_USERNAME": cluster_username,
+        "CLUSTER_PASSWORD": cluster_password,
+        "CLUSTER_API_URL": cluster_host,
+        # Optional pass-throughs only if needed by script runtime:
+        "PATH": os.environ.get("PATH", ""),
+        "HOME": os.environ.get("HOME", ""),
+    }

Also applies to: 50-57

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@utilities/upgrade.py` around lines 37 - 45, Replace the os.environ.copy()
usage with a minimal allowlist environment instead of forwarding the full parent
env: create a new dict (e.g., env = {}) and explicitly set only the required
keys (MTV_VERSION, MTV_SOURCE, IMAGE_INDEX, CLUSTER_USERNAME, CLUSTER_PASSWORD,
CLUSTER_API_URL) and, if necessary, a safe PATH from os.environ.get("PATH")
rather than copying everything; update both the env construction shown (the env
= os.environ.copy(); env.update({...}) block) and the similar block at lines
50-57 to use this explicit allowlist approach so no other CI/runtime secrets are
passed to the external upgrade script.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@utilities/upgrade.py`:
- Around line 37-45: Validate inputs before building env: explicitly check that
script_path, mtv_version, mtv_source, and py_config are not None (and that
mtv_source is a string) and that py_config contains required keys
"cluster_username", "cluster_password", and "cluster_host"; if any check fails
raise a clear ValueError mentioning the missing/invalid field. Do these checks
in the same scope where env is constructed (the block that references
mtv_version, mtv_source, image_index and py_config) so you never call
mtv_source.upper() or access py_config[...] when values may be None or missing;
after validation you can safely build env as shown.
- Around line 37-45: Replace the os.environ.copy() usage with a minimal
allowlist environment instead of forwarding the full parent env: create a new
dict (e.g., env = {}) and explicitly set only the required keys (MTV_VERSION,
MTV_SOURCE, IMAGE_INDEX, CLUSTER_USERNAME, CLUSTER_PASSWORD, CLUSTER_API_URL)
and, if necessary, a safe PATH from os.environ.get("PATH") rather than copying
everything; update both the env construction shown (the env = os.environ.copy();
env.update({...}) block) and the similar block at lines 50-57 to use this
explicit allowlist approach so no other CI/runtime secrets are passed to the
external upgrade script.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2a9a960a-de41-4c5d-9006-fd626c9d51ea

📥 Commits

Reviewing files that changed from the base of the PR and between 0cd8377 and 3d6341b.

📒 Files selected for processing (3)
  • Dockerfile
  • README.md
  • utilities/upgrade.py

coderabbitai[bot]
coderabbitai Bot previously approved these changes May 25, 2026
@krcmarik
Copy link
Copy Markdown
Contributor Author

/verified

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 26, 2026

Code review by qodo was updated up to the latest commit 31a264c

Comment thread tests/warm/test_mtv_warm_migration.py
@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented May 26, 2026

Code review by qodo was updated up to the latest commit 31a264c

@krcmarik
Copy link
Copy Markdown
Contributor Author

/verified

@myakove
Copy link
Copy Markdown
Collaborator

myakove commented May 28, 2026

/lgtm
/approve

@redhat-qe-bot
Copy link
Copy Markdown

Cherry-pick conflicts were resolved by AI

Cherry-picked PR feat: add MTV operator upgrade/update migration testing into v2.11: #521
Conflicts were automatically resolved by AI (claude/claude-opus-4-6[1m]).

Manual verification is required — please review the changes and test before merging.

@redhat-qe-bot
Copy link
Copy Markdown

Cherry-pick conflicts were resolved by AI

Cherry-picked PR feat: add MTV operator upgrade/update migration testing into v2.10: #522
Conflicts were automatically resolved by AI (claude/claude-opus-4-6[1m]).

Manual verification is required — please review the changes and test before merging.

@redhat-qe-bot
Copy link
Copy Markdown

New container for ghcr.io/redhatqe/mtv-api-tests:latest published

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants