diff --git a/tests/infrastructure/golden_images/update_boot_source/conftest.py b/tests/infrastructure/golden_images/update_boot_source/conftest.py index 3f10208aa2..bd65f25041 100644 --- a/tests/infrastructure/golden_images/update_boot_source/conftest.py +++ b/tests/infrastructure/golden_images/update_boot_source/conftest.py @@ -39,6 +39,11 @@ LOGGER = logging.getLogger(__name__) +@pytest.fixture() +def data_source_by_name_scope_function(request, admin_client, golden_images_namespace): + return DataSource(client=admin_client, name=request.param, namespace=golden_images_namespace.name) + + @pytest.fixture() def enabled_common_boot_image_import_feature_gate_scope_function( admin_client, diff --git a/tests/infrastructure/golden_images/update_boot_source/test_ssp_data_sources.py b/tests/infrastructure/golden_images/update_boot_source/test_ssp_data_sources.py index 9c67333220..909ef28d21 100644 --- a/tests/infrastructure/golden_images/update_boot_source/test_ssp_data_sources.py +++ b/tests/infrastructure/golden_images/update_boot_source/test_ssp_data_sources.py @@ -5,7 +5,6 @@ from kubernetes.dynamic.exceptions import ResourceNotFoundError from ocp_resources.data_import_cron import DataImportCron from ocp_resources.data_source import DataSource -from ocp_resources.datavolume import DataVolume from ocp_resources.resource import ResourceEditor from pytest_testconfig import py_config from timeout_sampler import TimeoutExpiredError, TimeoutSampler @@ -16,25 +15,19 @@ DATA_SOURCE_READY_FOR_CONSUMPTION_MESSAGE, PVC_NOT_FOUND_ERROR, ) -from tests.utils import get_parameters_from_template -from utilities.artifactory import ( - cleanup_artifactory_secret_and_config_map, - get_artifactory_config_map, - get_artifactory_secret, - get_http_image_url, +from tests.infrastructure.golden_images.update_boot_source.utils import ( + dv_for_data_source, + wait_for_data_source_unchanged_referenced_volume, ) -from utilities.constants import Images +from tests.utils import get_parameters_from_template from utilities.constants.hco import DATA_SOURCE_NAME from utilities.constants.images import DEFAULT_FEDORA_REGISTRY_URL from utilities.constants.pytest import QUARANTINED -from utilities.constants.storage import BIND_IMMEDIATE_ANNOTATION from utilities.constants.timeouts import ( TIMEOUT_5MIN, TIMEOUT_10MIN, ) -from utilities.exceptions import ResourceValueError from utilities.ssp import wait_for_condition_message_value -from utilities.storage import construct_datavolume_source_dict LOGGER = logging.getLogger(__name__) @@ -45,37 +38,6 @@ pytestmark = pytest.mark.post_upgrade -@contextmanager -def dv_for_data_source(name, data_source, admin_client): - artifactory_secret = get_artifactory_secret(namespace=data_source.namespace) - artifactory_config_map = get_artifactory_config_map(namespace=data_source.namespace) - with DataVolume( - client=admin_client, - name=name, - namespace=data_source.namespace, - source_dict=construct_datavolume_source_dict( - # underlying OS is not relevant - source="http", - url=get_http_image_url(image_directory=Images.Cirros.DIR, image_name=Images.Cirros.QCOW2_IMG), - secret_name=artifactory_secret.name, - cert_configmap_name=artifactory_config_map.name, - ), - size=Images.Cirros.DEFAULT_DV_SIZE, - storage_class=py_config["default_storage_class"], - annotations=BIND_IMMEDIATE_ANNOTATION, - api_name="storage", - ) as dv: - dv.wait_for_dv_success() - wait_for_condition_message_value( - resource=data_source, - expected_message=DATA_SOURCE_READY_FOR_CONSUMPTION_MESSAGE, - ) - yield dv - cleanup_artifactory_secret_and_config_map( - artifactory_secret=artifactory_secret, artifactory_config_map=artifactory_config_map - ) - - def opt_in_status_str(opt_in): return f"opt-{'in' if opt_in else 'out'}" @@ -97,23 +59,6 @@ def wait_for_data_source_reconciliation_after_update( raise -def wait_for_data_source_unchanged_referenced_volume(data_source, volume_name): - try: - for sample in TimeoutSampler( - wait_timeout=TIMEOUT_10MIN, - sleep=5, - func=lambda: data_source.source.name != volume_name, - ): - if sample: - raise ResourceValueError( - f"DataSource {data_source.name} volume reference was updated, " - f"expected {volume_name}, " - f"spec: {data_source.instance.spec}" - ) - except TimeoutExpiredError: - return - - def wait_for_data_source_updated_referenced_volume(data_source, volume_name): try: for sample in TimeoutSampler( @@ -264,11 +209,6 @@ def data_sources_from_templates_scope_function(admin_client, data_sources_names_ ] -@pytest.fixture() -def data_source_by_name_scope_function(request, unprivileged_client, golden_images_namespace): - return DataSource(client=unprivileged_client, name=request.param, namespace=golden_images_namespace.name) - - @pytest.fixture(scope="class") def data_source_by_name_scope_class(request, admin_client, golden_images_namespace): return DataSource(client=admin_client, name=request.param, namespace=golden_images_namespace.name) diff --git a/tests/infrastructure/golden_images/update_boot_source/utils.py b/tests/infrastructure/golden_images/update_boot_source/utils.py index 66d70159bf..bcd3d44e89 100644 --- a/tests/infrastructure/golden_images/update_boot_source/utils.py +++ b/tests/infrastructure/golden_images/update_boot_source/utils.py @@ -1,33 +1,41 @@ import logging import re +from contextlib import contextmanager import requests from bs4 import BeautifulSoup from kubernetes.dynamic import DynamicClient from ocp_resources.data_source import DataSource +from ocp_resources.datavolume import DataVolume from ocp_resources.namespace import Namespace from ocp_resources.persistent_volume_claim import PersistentVolumeClaim from ocp_resources.template import Template from ocp_resources.volume_snapshot import VolumeSnapshot from packaging.version import Version +from pytest_testconfig import py_config from timeout_sampler import TimeoutExpiredError, TimeoutSampler +from tests.infrastructure.golden_images.constants import DATA_SOURCE_READY_FOR_CONSUMPTION_MESSAGE +from utilities.constants import Images from utilities.constants.images import DEFAULT_FEDORA_REGISTRY_URL -from utilities.constants.storage import WILDCARD_CRON_EXPRESSION +from utilities.constants.storage import BIND_IMMEDIATE_ANNOTATION, REGISTRY_STR, WILDCARD_CRON_EXPRESSION from utilities.constants.timeouts import ( TIMEOUT_2MIN, TIMEOUT_5MIN, TIMEOUT_5SEC, TIMEOUT_30SEC, ) +from utilities.exceptions import ResourceValueError from utilities.infra import generate_openshift_pull_secret_file from utilities.ssp import ( get_data_import_crons, matrix_auto_boot_data_import_cron_prefixes, + wait_for_condition_message_value, ) from utilities.storage import ( DATA_IMPORT_CRON_SUFFIX, RESOURCE_MANAGED_BY_DATA_IMPORT_CRON_LABEL, + construct_datavolume_source_dict, ) from utilities.virt import get_oc_image_info @@ -234,3 +242,69 @@ def wait_for_created_volume_from_data_import_cron(custom_data_source: DataSource f"DataSource conditions: {custom_data_source.instance.get('status', {}).get('conditions')}" ) raise + + +@contextmanager +def dv_for_data_source(name, data_source, admin_client): + """ + Creates a registry-sourced DataVolume using the default Fedora image, waits for the DV to + succeed, then waits for the DataSource to report it is ready for consumption. The underlying + OS choice is irrelevant — the DV is used only to provide a backing volume for the DataSource. + + Args: + name (str): Name for the DataVolume to create. + data_source (DataSource): The DataSource whose namespace the DV is created in. + The DataSource is also monitored for readiness after the DV succeeds. + admin_client (DynamicClient): Admin client used to create the DataVolume. + + Yields: + DataVolume: The created and ready DataVolume resource. + """ + with DataVolume( + client=admin_client, + name=name, + namespace=data_source.namespace, + source_dict=construct_datavolume_source_dict(source=REGISTRY_STR, url=DEFAULT_FEDORA_REGISTRY_URL), + size=Images.Fedora.DEFAULT_DV_SIZE, + storage_class=py_config["default_storage_class"], + annotations=BIND_IMMEDIATE_ANNOTATION, + api_name="storage", + ) as dv: + dv.wait_for_dv_success() + wait_for_condition_message_value( + resource=data_source, + expected_message=DATA_SOURCE_READY_FOR_CONSUMPTION_MESSAGE, + ) + yield dv + + +def wait_for_data_source_unchanged_referenced_volume(data_source, volume_name): + """ + Assert that a DataSource's referenced volume does not change within a short observation window. + + Polls the DataSource and raises ResourceValueError immediately if the + source name diverges from ``volume_name``. A TimeoutExpiredError (meaning no change was + observed) is treated as success and suppressed. + + Args: + data_source (DataSource): The DataSource resource to observe. + volume_name (str): The volume name that must remain as the DataSource's source reference. + + Raises: + ResourceValueError: If the DataSource's source reference changes to a different volume + before the observation window expires. + """ + try: + for sample in TimeoutSampler( + wait_timeout=TIMEOUT_30SEC, + sleep=TIMEOUT_5SEC, + func=lambda: data_source.source.name != volume_name, + ): + if sample: + raise ResourceValueError( + f"DataSource {data_source.name} volume reference was updated, " + f"expected {volume_name}, " + f"spec: {data_source.instance.spec}" + ) + except TimeoutExpiredError: + return