From 8bd33ffbcc6ea60e9fcf6ace0ab51ea4384e0862 Mon Sep 17 00:00:00 2001 From: Harel Meir Date: Tue, 9 Jun 2026 10:24:05 +0300 Subject: [PATCH] [4.18] eus upgrade alignment Signed-off-by: Harel Meir --- conftest.py | 15 +- tests/conftest.py | 19 +- .../product_upgrade/conftest.py | 131 +++++------- .../product_upgrade/test_eus_upgrade.py | 12 +- .../product_upgrade/utils.py | 195 ++++++++++++++---- tests/virt/upgrade/conftest.py | 37 ++-- tests/virt/upgrade/test_upgrade_virt.py | 2 +- tests/virt/upgrade/utils.py | 18 +- tox.ini | 2 +- 9 files changed, 251 insertions(+), 180 deletions(-) diff --git a/conftest.py b/conftest.py index ffce269b8c..9ee2182e14 100644 --- a/conftest.py +++ b/conftest.py @@ -22,6 +22,7 @@ from _pytest.runner import CallInfo from kubernetes.dynamic.exceptions import ConflictError from ocp_resources.resource import get_client +from packaging.version import Version from pyhelper_utils.shell import run_command from pytest import Item from pytest_testconfig import config as py_config @@ -150,7 +151,6 @@ def pytest_addoption(parser): "--eus-ocp-images", help="Comma-separated OCP images to use for EUS-to-EUS upgrade.", ) - install_upgrade_group.addoption("--eus-cnv-target-version", help="target CNV version for eus upgrade") install_upgrade_group.addoption( "--upgrade-skip-default-sc-setup", help="Skip the fixture that changes the default sc in upgrade lane", @@ -302,18 +302,21 @@ def pytest_cmdline_main(config): if upgrade_option == "ocp" and not config.getoption("ocp_image"): raise ValueError("Running with --upgrade ocp: Missing --ocp-image") - if upgrade_option == "cnv": + if upgrade_option in ("cnv", "eus"): if not config.getoption("cnv_version"): raise ValueError("Missing --cnv-version") if not config.getoption("cnv_image"): - if config.getoption("cnv_source") != "production": + if upgrade_option == "eus" or config.getoption("cnv_source") != "production": raise ValueError("Missing --cnv-image") - if upgrade_option == "eus": + if upgrade_option == "eus" and not config.option.collectonly: + cnv_version = config.getoption("cnv_version") + if Version(version=cnv_version).minor % 2: + raise ValueError(f"EUS target version {cnv_version} must have an even minor version") eus_ocp_images = config.getoption("eus_ocp_images") if not (eus_ocp_images and len(eus_ocp_images.split(",")) == 2): raise ValueError( - f"Two OCP images are needed to perform EUS-to-EUS upgrade with --eus-ocp-images." + f"Two OCP images are needed for EUS-to-EUS upgrade with --eus-ocp-images." f" Provided images: {eus_ocp_images}" ) @@ -793,7 +796,7 @@ def is_skip_must_gather(node: Node) -> bool: def get_inspect_command_namespace_string(node: Node, test_name: str) -> str: namespace_str = "" - components = [key for key in NAMESPACE_COLLECTION.keys() if f"tests/{key}/" in test_name] + components = [key for key in NAMESPACE_COLLECTION if f"tests/{key}/" in test_name] if not components: LOGGER.warning(f"{test_name} does not require special data collection on failure") else: diff --git a/tests/conftest.py b/tests/conftest.py index 4fe5b50849..d06e0e3ead 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -63,7 +63,7 @@ from ocp_resources.virtual_machine_instancetype import VirtualMachineInstancetype from ocp_resources.virtual_machine_preference import VirtualMachinePreference from ocp_utilities.monitoring import Prometheus -from packaging.version import Version, parse +from packaging.version import parse from pytest_testconfig import config as py_config from timeout_sampler import TimeoutSampler @@ -1898,27 +1898,14 @@ def hco_target_csv_name(cnv_target_version): return get_hco_csv_name_by_version(cnv_target_version=cnv_target_version) if cnv_target_version else None -@pytest.fixture(scope="session") -def eus_hco_target_csv_name(eus_target_cnv_version): - return get_hco_csv_name_by_version(cnv_target_version=eus_target_cnv_version) - - @pytest.fixture(scope="session") def cnv_target_version(pytestconfig): return pytestconfig.option.cnv_version @pytest.fixture(scope="session") -def eus_target_cnv_version(pytestconfig, cnv_current_version): - cnv_current_version = Version(version=cnv_current_version) - minor = cnv_current_version.minor - # EUS-to-EUS upgrades are only viable between even-numbered minor versions, exit if non-eus version - if minor % 2: - exit_pytest_execution( - message=f"EUS upgrade can not be performed from non-eus version: {cnv_current_version}", - return_code=EUS_ERROR_CODE, - ) - return pytestconfig.option.eus_cnv_target_version or f"{cnv_current_version.major}.{minor + 2}.0" +def cnv_channel(pytestconfig): + return pytestconfig.option.cnv_channel @pytest.fixture() diff --git a/tests/install_upgrade_operators/product_upgrade/conftest.py b/tests/install_upgrade_operators/product_upgrade/conftest.py index da29648752..3e87f0ba5d 100644 --- a/tests/install_upgrade_operators/product_upgrade/conftest.py +++ b/tests/install_upgrade_operators/product_upgrade/conftest.py @@ -15,13 +15,12 @@ ) from tests.install_upgrade_operators.product_upgrade.utils import ( approve_cnv_upgrade_install_plan, + build_eus_upgrade_path_dict, extract_ocp_version_from_ocp_image, get_alerts_fired_during_upgrade, get_all_firing_cnv_alerts, - get_iib_images_of_cnv_versions, get_nodes_labels, get_nodes_taints, - get_upgrade_path, perform_cnv_upgrade, run_ocp_upgrade_command, set_workload_update_methods_hco, @@ -49,8 +48,8 @@ get_data_collector_base_directory, ) from utilities.infra import ( + exit_pytest_execution, generate_openshift_pull_secret_file, - get_csv_by_name, get_prometheus_k8s_token, get_related_images_name_and_version, get_subscription, @@ -66,6 +65,7 @@ LOGGER = logging.getLogger(__name__) POD_STR_NOT_MANAGED_BY_HCO = "hostpath-" +EUS_ERROR_CODE = 98 @pytest.fixture(scope="session") @@ -323,64 +323,25 @@ def fired_alerts_during_upgrade( @pytest.fixture(scope="session") -def eus_shortest_upgrade_path_info(eus_target_cnv_version, cnv_current_version): - # if cant get from stable - try candidate - if upgrade_paths_target_version := get_upgrade_path(target_version=eus_target_cnv_version): - target_channel = "stable" - else: - target_channel = "candidate" - upgrade_paths_target_version = get_upgrade_path(target_version=eus_target_cnv_version, channel=target_channel) - assert upgrade_paths_target_version, f"Couldn't find upgrade path for {eus_target_cnv_version} version" - - sorted_upgrade_paths = sorted( - upgrade_paths_target_version, key=lambda path: Version(version=str(path["startVersion"])), reverse=True - ) - for path in sorted_upgrade_paths: - if intermediate_upgrade_paths := get_upgrade_path(target_version=path["startVersion"]): - if intermediate_path_dict := next( - (item for item in intermediate_upgrade_paths if item["startVersion"] == f"v{cnv_current_version}"), None - ): - return { - "target_versions": path["versions"], - "intermediate_versions": intermediate_path_dict["versions"], - "target_channel": target_channel, - } - raise AssertionError(f"Couldn't find upgrade path for {eus_target_cnv_version} version from {cnv_current_version}") - - -@pytest.fixture(scope="session") -def eus_target_channel(eus_shortest_upgrade_path_info): - return eus_shortest_upgrade_path_info["target_channel"] - - -@pytest.fixture(scope="session") -def eus_cnv_upgrade_path(eus_shortest_upgrade_path_info, eus_target_channel): - if not ( - target_paths := get_iib_images_of_cnv_versions( - versions=eus_shortest_upgrade_path_info["target_versions"], - target_channel=eus_target_channel, - ) - ): - target_paths = get_iib_images_of_cnv_versions( - versions=eus_shortest_upgrade_path_info["target_versions"], - errata_status="false", - target_channel=eus_target_channel, +def eus_cnv_upgrade_path( + cnv_target_version, + cnv_current_version, + cnv_channel, + cnv_image_url, +): + if Version(version=cnv_current_version).minor % 2: + exit_pytest_execution( + message=f"EUS upgrade can not be performed from non-eus version: {cnv_current_version}", + return_code=EUS_ERROR_CODE, + filename="eus_upgrade_failure.txt", ) - intermediate_paths = get_iib_images_of_cnv_versions( - versions=eus_shortest_upgrade_path_info["intermediate_versions"], - ) - assert intermediate_paths, ( - f"Couldn't find build info for {eus_shortest_upgrade_path_info['intermediate_versions']} versions" + return build_eus_upgrade_path_dict( + current_cnv_version=cnv_current_version, + target_cnv_version=cnv_target_version, + target_channel=cnv_channel, + target_cnv_image_url=cnv_image_url, ) - # Return a dictionary with the versions and images for the EUS-to-EUS upgrade - upgrade_path_dict = { - EUS: target_paths, - "non-eus": intermediate_paths, - } - LOGGER.info(f"Upgrade path for EUS-to-EUS upgrade: {upgrade_path_dict}") - return upgrade_path_dict - @pytest.fixture(scope="session") def default_workload_update_strategy(hyperconverged_resource_scope_session): @@ -564,18 +525,25 @@ def source_eus_to_non_eus_cnv_upgraded( admin_client, hco_namespace, eus_cnv_upgrade_path, - eus_target_channel, + cnv_subscription_scope_session, + cnv_registry_source, hyperconverged_resource_scope_function, - updated_cnv_subscription_source, ): - for version, cnv_image in sorted(eus_cnv_upgrade_path["non-eus"].items()): + for version, build_info in sorted( + eus_cnv_upgrade_path["non-eus"].items(), + key=lambda item: Version(version=item[0]), + ): + cnv_image = build_info["cnv_image_url"] LOGGER.info(f"Cnv upgrade to version {version} using image: {cnv_image}") perform_cnv_upgrade( admin_client=admin_client, cnv_image_url=cnv_image, cr_name=hyperconverged_resource_scope_function.name, hco_namespace=hco_namespace, - cnv_target_version=version.lstrip("v"), + cnv_target_version=version, + subscription=cnv_subscription_scope_session, + subscription_source=cnv_registry_source["cnv_subscription_source"], + subscription_channel=build_info["channel"], ) LOGGER.info("Successfully performed cnv upgrades from source EUS to non-EUS version.") @@ -585,32 +553,27 @@ def non_eus_to_target_eus_cnv_upgraded( admin_client, hco_namespace, eus_cnv_upgrade_path, - eus_target_channel, cnv_subscription_scope_session, cnv_registry_source, hyperconverged_resource_scope_function, ): - version, cnv_image = next(iter(eus_cnv_upgrade_path[EUS].items())) - LOGGER.info(f"Cnv upgrade to version {version} using image: {cnv_image}") - perform_cnv_upgrade( - admin_client=admin_client, - cnv_image_url=cnv_image, - cr_name=hyperconverged_resource_scope_function.name, - hco_namespace=hco_namespace, - cnv_target_version=version.lstrip("v"), - subscription=cnv_subscription_scope_session, - subscription_source=cnv_registry_source["cnv_subscription_source"], - subscription_channel=eus_target_channel, - ) - - -@pytest.fixture() -def eus_created_target_hco_csv(admin_client, hco_namespace, eus_hco_target_csv_name): - return get_csv_by_name( - csv_name=eus_hco_target_csv_name, - admin_client=admin_client, - namespace=hco_namespace.name, - ) + for version, build_info in sorted( + eus_cnv_upgrade_path[EUS].items(), + key=lambda item: Version(version=item[0]), + ): + cnv_image = build_info["cnv_image_url"] + LOGGER.info(f"Cnv upgrade to version {version} using image: {cnv_image}") + perform_cnv_upgrade( + admin_client=admin_client, + cnv_image_url=cnv_image, + cr_name=hyperconverged_resource_scope_function.name, + hco_namespace=hco_namespace, + cnv_target_version=version, + subscription=cnv_subscription_scope_session, + subscription_source=cnv_registry_source["cnv_subscription_source"], + subscription_channel=build_info["channel"], + ) + LOGGER.info("Successfully performed cnv upgrades from non-EUS to target EUS version.") @pytest.fixture() diff --git a/tests/install_upgrade_operators/product_upgrade/test_eus_upgrade.py b/tests/install_upgrade_operators/product_upgrade/test_eus_upgrade.py index 7b666946c3..b063aa2ff0 100644 --- a/tests/install_upgrade_operators/product_upgrade/test_eus_upgrade.py +++ b/tests/install_upgrade_operators/product_upgrade/test_eus_upgrade.py @@ -8,6 +8,11 @@ LOGGER = logging.getLogger(__name__) +pytestmark = pytest.mark.usefixtures( + "nodes_taints_before_upgrade", + "nodes_labels_before_upgrade", +) + @pytest.mark.product_upgrade_test @pytest.mark.upgrade @@ -20,10 +25,7 @@ def test_eus_upgrade_process( self, admin_client, hco_namespace, - eus_target_cnv_version, eus_cnv_upgrade_path, - worker_machine_config_pools_conditions, - eus_applied_all_icsp, eus_paused_worker_mcp, eus_paused_workload_update, source_eus_to_non_eus_ocp_upgraded, @@ -31,7 +33,7 @@ def test_eus_upgrade_process( upgraded_odf, non_eus_to_target_eus_ocp_upgraded, non_eus_to_target_eus_cnv_upgraded, - eus_created_target_hco_csv, + created_target_hco_csv, eus_unpaused_workload_update, eus_unpaused_worker_mcp, ): @@ -39,6 +41,6 @@ def test_eus_upgrade_process( verify_upgrade_cnv( dyn_client=admin_client, hco_namespace=hco_namespace, - expected_images=get_related_images_name_and_version(csv=eus_created_target_hco_csv).values(), + expected_images=get_related_images_name_and_version(csv=created_target_hco_csv).values(), ) LOGGER.info("EUS post upgrade validation completed.") diff --git a/tests/install_upgrade_operators/product_upgrade/utils.py b/tests/install_upgrade_operators/product_upgrade/utils.py index 2869c7d8ee..d698912047 100644 --- a/tests/install_upgrade_operators/product_upgrade/utils.py +++ b/tests/install_upgrade_operators/product_upgrade/utils.py @@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Any if TYPE_CHECKING: + from ocp_resources.subscription import Subscription from ocp_utilities.monitoring import Prometheus from deepdiff import DeepDiff @@ -21,15 +22,15 @@ from ocp_resources.machine_config_pool import MachineConfigPool from ocp_resources.namespace import Namespace from ocp_resources.resource import Resource, ResourceEditor -from ocp_resources.subscription import Subscription +from packaging.version import Version from pyhelper_utils.shell import run_command from timeout_sampler import TimeoutExpiredError, TimeoutSampler from tests.install_upgrade_operators.constants import WORKLOAD_UPDATE_STRATEGY_KEY_NAME, WORKLOADUPDATEMETHODS from tests.install_upgrade_operators.utils import wait_for_install_plan +from tests.upgrade_params import EUS from utilities.constants import ( BASE_EXCEPTIONS_DICT, - BREW_REGISTERY_SOURCE, FIRING_STATE, HCO_CATALOG_SOURCE, IMAGE_CRON_STR, @@ -52,6 +53,7 @@ get_deployments, get_pod_by_name_prefix, get_pods, + stable_channel_released_to_prod, wait_for_consistent_resource_conditions, wait_for_version_explorer_response, ) @@ -458,6 +460,7 @@ def verify_upgrade_ocp( machine_config_pools_list=machine_config_pools_list, initial_mcp_conditions=initial_mcp_conditions, nodes=nodes, + timeout=TIMEOUT_180MIN, ) wait_for_cluster_version_stable_conditions( @@ -552,46 +555,162 @@ def get_alerts_fired_during_upgrade( return alerts_by_name -def get_upgrade_path(target_version: str, channel: str = "stable") -> list[dict[str, Any]]: - paths = wait_for_version_explorer_response( +def get_upgrade_path(target_version: str, channel: str = "stable") -> dict[str, Any]: + """Query the version explorer for upgrade paths to a target CNV version on a given channel.""" + return wait_for_version_explorer_response( api_end_point="GetUpgradePath", query_string=f"targetVersion={target_version}&channel={channel}", ) - return paths["path"] -def get_iib_images_of_cnv_versions( - versions: list[str], errata_status: str = "true", target_channel: str = "stable" -) -> dict[str, Any] | None: - def _find_channel_build(channels_dict: list[dict], channel: str) -> dict | None: - return next((build_dict for build_dict in channels_dict if build_dict["channel"] == channel), None) +def find_path_with_start_version(paths: list[dict[str, Any]], start_version: str) -> dict[str, Any] | None: + """Find an upgrade path entry matching start_version.""" + for path in paths: + if str(path["startVersion"]).lstrip("v") == start_version: + return path + return None + + +def get_cnv_image_url_for_version(version: str, channel: str) -> str: + """Return the CNV image URL of the first successful build for a version on a given channel.""" + builds = get_build_info_by_version(version=version, channel=channel)["successful_builds"] + assert builds, f"No successful builds for {version} on {channel} channel" + return builds[0]["iib"] - target_version_images = {} - for version in versions: - successful_builds = get_build_info_by_version(version=version, errata_status=errata_status)["successful_builds"] +def get_stable_released_builds(minor_version: str) -> list[dict[str, Any]]: + """Return stable released builds for a minor version, sorted newest-first.""" + builds = wait_for_version_explorer_response( + api_end_point="GetReleasedBuilds", + query_string=f"minor_version={minor_version}&stage=false", + )["builds"] + stable_builds = sorted( + [build for build in builds if stable_channel_released_to_prod(channels=build["channels"])], + key=lambda build: Version(version=build["csv_version"]), + reverse=True, + ) + assert stable_builds, f"No stable released builds for {minor_version}" + return stable_builds + - if errata_status == "true": - # Look for builds with QE or SHIPPED_LIVE errata status - if not (successful_builds or successful_builds[0]["errata_status"] in ["QE", "SHIPPED_LIVE"]): - return None +def get_stable_channel_iib(build: dict[str, Any]) -> str: + """Extract the IIB URL from a build's stable channel entry.""" + stable_entry = next( + (item for item in build["channels"] if item.get("channel") == "stable" and item.get("released_to_prod")), + None, + ) + if not stable_entry: + raise ValueError(f"No stable released channel entry in build: {build}") + return stable_entry["iib"] + + +def build_version_images( + versions: list[str], + target_version: str, + target_cnv_image_url: str, + channel: str = "stable", +) -> dict[str, dict[str, str]]: + """Build a {version: {"cnv_image_url": ..., "channel": ...}} dict from a versions list.""" + images: dict[str, dict[str, str]] = {} + for raw_version in versions: + version = raw_version.lstrip("v") + if version == target_version: + images[version] = {"cnv_image_url": target_cnv_image_url, "channel": channel} else: - successful_builds = get_build_info_by_version(version=version, errata_status=errata_status)[ - "successful_builds" - ] - for build in successful_builds: - if build_info_dict := _find_channel_build(channels_dict=build["channels"], channel=target_channel): - break - assert build_info_dict, f"Couldn't find build info for {version} version in {target_channel} channel" - target_version_images[version] = f"{BREW_REGISTERY_SOURCE}/rh-osbs/iib:{build_info_dict['iib'].split(':')[-1]}" + images[version] = { + "cnv_image_url": get_cnv_image_url_for_version(version=version, channel=channel), + "channel": channel, + } + return images + + +def find_intermediate_cnv_versions( + current_version: str, + target_version: str, + target_channel: str = "stable", +) -> tuple[dict[str, dict[str, str]], dict[str, Any]]: + """Find the intermediate CNV versions for an EUS-to-EUS upgrade. + + Walks stable z-streams for the intermediate minor (current+1) from latest to oldest, + picks the first valid startVersion in the target's upgrade path, then resolves the + full hop path from current to intermediate. + + Returns: + Tuple of (non_eus_images, target_step). + """ + target_paths = get_upgrade_path(target_version=target_version, channel=target_channel)["path"] + assert target_paths, f"No upgrade paths for {target_version} on {target_channel} channel" + + current = Version(version=current_version) + intermediate_minor = f"{current.major}.{current.minor + 1}" + stable_builds = get_stable_released_builds(minor_version=intermediate_minor) + + for build in stable_builds: + intermediate_version = build["csv_version"].lstrip("v") + target_step = find_path_with_start_version(paths=target_paths, start_version=intermediate_version) + if not target_step: + continue + + LOGGER.info(f"Using {intermediate_version} as intermediate version for EUS upgrade to {target_version}") + intermediate_iib = get_stable_channel_iib(build=build) + + intermediate_paths = get_upgrade_path(target_version=intermediate_version, channel="stable")["path"] + intermediate_step = find_path_with_start_version(paths=intermediate_paths, start_version=current_version) + + if intermediate_step: + non_eus_images = build_version_images( + versions=intermediate_step["versions"], + target_version=intermediate_version, + target_cnv_image_url=intermediate_iib, + ) + else: + non_eus_images = {intermediate_version: {"cnv_image_url": intermediate_iib, "channel": "stable"}} + + return non_eus_images, target_step + + raise AssertionError(f"No valid intermediate in {intermediate_minor} for upgrade to {target_version}") + - assert target_version_images, f"Couldn't find build info for {versions} versions" - return target_version_images +def build_eus_upgrade_path_dict( + current_cnv_version: str, + target_cnv_version: str, + target_channel: str = "stable", + target_cnv_image_url: str | None = None, +) -> dict[str, dict[str, dict[str, str]]]: + """Build the EUS-to-EUS upgrade path with IIB images for each phase. + + Args: + current_cnv_version: Currently installed CNV version (e.g., "4.20.15"). + target_cnv_version: EUS target CNV version (e.g., "4.22.0"). + target_channel: Channel for the target GetUpgradePath query (default "stable"). + target_cnv_image_url: CNV image URL for the EUS target (from --cnv-image CLI arg). + + Returns: + Dict with "non-eus" and "eus" keys, each mapping version string + to a dict with "cnv_image_url" and "channel" keys. + """ + assert target_cnv_image_url, "target_cnv_image_url is required for EUS upgrade path" + non_eus_images, target_step = find_intermediate_cnv_versions( + current_version=current_cnv_version, + target_version=target_cnv_version, + target_channel=target_channel, + ) + eus_images = build_version_images( + versions=target_step["versions"], + target_version=target_cnv_version, + target_cnv_image_url=target_cnv_image_url, + channel=target_channel, + ) + upgrade_path = {"non-eus": non_eus_images, EUS: eus_images} + LOGGER.info(f"Upgrade path for EUS-to-EUS upgrade: {upgrade_path}") + return upgrade_path -def get_build_info_by_version(version: str, errata_status: str = "true") -> dict[str, Any]: +def get_build_info_by_version(version: str, channel: str = "stable") -> dict[str, Any]: + """Get successful builds for a CNV version from the version explorer, filtered by channel.""" return wait_for_version_explorer_response( - api_end_point="GetSuccessfulBuildsByVersion", query_string=f"version={version}&errata_status={errata_status}" + api_end_point="GetSuccessfulBuildsByVersion", + query_string=f"version={version}&channel={channel}", ) @@ -620,10 +739,20 @@ def perform_cnv_upgrade( cnv_target_version: str, subscription: Subscription | None = None, subscription_source: str | None = None, - subscription_channel: str = "", + subscription_channel: str | None = None, ) -> None: hco_target_csv_name = get_hco_csv_name_by_version(cnv_target_version=cnv_target_version) + if subscription or subscription_source or subscription_channel: + assert subscription and subscription_source and subscription_channel, ( + "subscription, subscription_source, and subscription_channel must all be provided together" + ) + update_subscription_source( + subscription=subscription, + subscription_source=subscription_source, + subscription_channel=subscription_channel, + ) + LOGGER.info("Updating image in CatalogSource") update_image_in_catalog_source( dyn_client=admin_client, @@ -631,12 +760,6 @@ def perform_cnv_upgrade( catalog_source_name=HCO_CATALOG_SOURCE, cr_name=cr_name, ) - if subscription and subscription_source: - update_subscription_source( - subscription=subscription, - subscription_source=subscription_source, - subscription_channel=subscription_channel, - ) LOGGER.info("Approving CNV InstallPlan") approve_cnv_upgrade_install_plan( dyn_client=admin_client, diff --git a/tests/virt/upgrade/conftest.py b/tests/virt/upgrade/conftest.py index 1314fe0918..5f859afa19 100644 --- a/tests/virt/upgrade/conftest.py +++ b/tests/virt/upgrade/conftest.py @@ -15,7 +15,7 @@ from tests.virt.constants import VM_LABEL from tests.virt.upgrade.utils import ( get_all_migratable_vms, - get_virt_launcher_image_from_csv, + get_virt_launcher_images_from_csv, get_vm_boot_time, validate_vms_pod_updated, vm_from_template, @@ -26,12 +26,10 @@ OS_FLAVOR_RHEL, TIMEOUT_30MIN, TIMEOUT_40MIN, - TIMEOUT_90MIN, Images, ) from utilities.hco import ResourceEditorValidateHCOReconcile from utilities.infra import ( - check_pod_disruption_budget_for_completed_migrations, get_csv_by_name, ) from utilities.storage import ( @@ -167,31 +165,20 @@ def migratable_vms(admin_client, hco_namespace, upgrade_namespaces): @pytest.fixture() def unupdated_vmi_pods_names( admin_client, - hco_namespace, - hco_target_csv_name, - eus_hco_target_csv_name, - upgrade_namespaces, migratable_vms, - virt_launcher_from_csv_before_upgrade, + virt_launcher_images_from_csv_before_upgrade, csv_after_upgrade, ): - virt_launcher_image_after_upgrade = get_virt_launcher_image_from_csv(csv=csv_after_upgrade) - - if virt_launcher_from_csv_before_upgrade == virt_launcher_image_after_upgrade: - LOGGER.warning(f"virt-launcher unchanged, skipping migration check: {virt_launcher_from_csv_before_upgrade}") + virt_launcher_images_after_upgrade = get_virt_launcher_images_from_csv(csv=csv_after_upgrade) + if virt_launcher_images_from_csv_before_upgrade == virt_launcher_images_after_upgrade: + LOGGER.warning( + f"virt-launcher unchanged, skipping migration check: {virt_launcher_images_from_csv_before_upgrade}" + ) return [] wait_for_automatic_vm_migrations(vm_list=migratable_vms) - - for ns in upgrade_namespaces: - LOGGER.info(f"Checking PodDisruptionBudget in namespaces: {ns.name}") - check_pod_disruption_budget_for_completed_migrations( - admin_client=admin_client, namespace=ns.name, timeout=TIMEOUT_90MIN - ) - return validate_vms_pod_updated( - admin_client=admin_client, - expected_virt_launcher_image=virt_launcher_image_after_upgrade, + expected_virt_launcher_images=virt_launcher_images_after_upgrade, vm_list=migratable_vms, ) @@ -369,14 +356,14 @@ def parallel_live_migrations_increased(hyperconverged_resource_scope_session): @pytest.fixture(scope="session") -def virt_launcher_from_csv_before_upgrade(csv_scope_session): - return get_virt_launcher_image_from_csv(csv=csv_scope_session) +def virt_launcher_images_from_csv_before_upgrade(csv_scope_session): + return get_virt_launcher_images_from_csv(csv=csv_scope_session) @pytest.fixture() -def csv_after_upgrade(admin_client, hco_namespace, hco_target_csv_name, eus_hco_target_csv_name): +def csv_after_upgrade(admin_client, hco_namespace, hco_target_csv_name): return get_csv_by_name( admin_client=admin_client, namespace=hco_namespace.name, - csv_name=hco_target_csv_name or eus_hco_target_csv_name, + csv_name=hco_target_csv_name, ) diff --git a/tests/virt/upgrade/test_upgrade_virt.py b/tests/virt/upgrade/test_upgrade_virt.py index d5858d1cfc..6586faf165 100644 --- a/tests/virt/upgrade/test_upgrade_virt.py +++ b/tests/virt/upgrade/test_upgrade_virt.py @@ -54,7 +54,7 @@ @pytest.mark.usefixtures( "base_templates", "parallel_live_migrations_increased", - "virt_launcher_from_csv_before_upgrade", + "virt_launcher_images_from_csv_before_upgrade", ) class TestUpgradeVirt: """Pre-upgrade tests""" diff --git a/tests/virt/upgrade/utils.py b/tests/virt/upgrade/utils.py index d7878d4718..95d0dcdffb 100644 --- a/tests/virt/upgrade/utils.py +++ b/tests/virt/upgrade/utils.py @@ -2,11 +2,16 @@ import shlex from contextlib import contextmanager from datetime import datetime +from typing import TYPE_CHECKING import pytest from ocp_resources.datavolume import DataVolume from ocp_resources.template import Template from ocp_resources.virtual_machine import VirtualMachine + +if TYPE_CHECKING: + from ocp_resources.cluster_service_version import ClusterServiceVersion + from ocp_resources.virtual_machine_instance_migration import ( VirtualMachineInstanceMigration, ) @@ -34,10 +39,9 @@ TIMESTAMP_FORMAT = "%Y-%m-%dT%H:%M:%SZ" -def get_virt_launcher_image_from_csv(csv): - for item in csv.instance.spec.relatedImages: - if VIRT_LAUNCHER in item["name"]: - return item["image"] +def get_virt_launcher_images_from_csv(csv: "ClusterServiceVersion") -> set[str]: + if images := {item["image"] for item in csv.instance.spec.relatedImages if VIRT_LAUNCHER in item["name"]}: + return images raise ValueError(f"Image digest for {VIRT_LAUNCHER} not found") @@ -165,11 +169,13 @@ def wait_for_automatic_vm_migrations(vm_list): raise -def validate_vms_pod_updated(admin_client, expected_virt_launcher_image, vm_list): +def validate_vms_pod_updated( + expected_virt_launcher_images: set[str], vm_list: list[VirtualMachine] +) -> list[dict[str, str]]: return [ {pod.name: pod.instance.spec.containers[0].image} for pod in [vm.vmi.virt_launcher_pod for vm in vm_list] - if pod.instance.spec.containers[0].image != expected_virt_launcher_image + if pod.instance.spec.containers[0].image not in expected_virt_launcher_images ] diff --git a/tox.ini b/tox.ini index 154d831c09..62fad609d9 100644 --- a/tox.ini +++ b/tox.ini @@ -19,7 +19,7 @@ commands = uv run pytest --collect-only uv run pytest --upgrade=cnv --cnv-image=NA --cnv-version=NA --collect-only uv run pytest --upgrade=ocp --ocp-image=NA --collect-only - uv run pytest --upgrade=eus --eus-ocp-images=NA,NA --collect-only + uv run pytest --upgrade=eus --cnv-image=NA --cnv-version=NA --eus-ocp-images=NA,NA --collect-only uv run pytest --upgrade_custom=cnv --cnv-image=NA --cnv-version=NA --collect-only uv run pytest --upgrade_custom=ocp --ocp-image=NA --collect-only uv run pytest --tc-file=tests/global_config_aws.py --collect-only