From 2562f93383889fe492a98170428a055fbbd0b073 Mon Sep 17 00:00:00 2001 From: James Falcon Date: Mon, 27 Mar 2023 14:34:12 -0500 Subject: integration tests: Refactor instance checking (#1989) Using individual release and platform marks to specify our test support matrix was leading to too many marks specifying different combinations of things. Rather, we can rely on the "skipif" mark to perform any needed release or platform checks. --- integration-requirements.txt | 1 + tests/integration_tests/bugs/test_gh626.py | 7 +- tests/integration_tests/bugs/test_gh632.py | 7 +- tests/integration_tests/bugs/test_gh668.py | 7 +- tests/integration_tests/bugs/test_gh671.py | 3 +- tests/integration_tests/bugs/test_gh868.py | 10 +-- tests/integration_tests/bugs/test_lp1835584.py | 16 ++-- tests/integration_tests/bugs/test_lp1897099.py | 6 +- tests/integration_tests/bugs/test_lp1898997.py | 12 ++- tests/integration_tests/bugs/test_lp1901011.py | 3 +- tests/integration_tests/bugs/test_lp1910835.py | 4 +- tests/integration_tests/bugs/test_lp1912844.py | 6 +- tests/integration_tests/clouds.py | 65 ++------------- tests/integration_tests/cmd/test_status.py | 16 ++-- tests/integration_tests/conftest.py | 25 ------ .../datasources/test_detect_openstack.py | 3 +- .../integration_tests/datasources/test_ec2_ipv6.py | 3 +- .../datasources/test_lxd_discovery.py | 15 ++-- .../datasources/test_lxd_hotplug.py | 17 ++-- .../datasources/test_network_dependency.py | 8 +- .../integration_tests/datasources/test_nocloud.py | 7 +- .../datasources/test_oci_networking.py | 7 +- .../datasources/test_tmp_noexec.py | 10 +-- tests/integration_tests/integration_settings.py | 6 +- tests/integration_tests/modules/test_ansible.py | 18 +++-- tests/integration_tests/modules/test_apt.py | 39 ++++----- tests/integration_tests/modules/test_ca_certs.py | 5 +- tests/integration_tests/modules/test_combined.py | 37 +++++---- tests/integration_tests/modules/test_disk_setup.py | 21 +++-- tests/integration_tests/modules/test_growpart.py | 8 +- tests/integration_tests/modules/test_hotplug.py | 26 ++++-- .../modules/test_keys_to_console.py | 13 +-- tests/integration_tests/modules/test_lxd.py | 46 +++++++---- .../modules/test_package_update_upgrade_install.py | 4 +- .../integration_tests/modules/test_persistence.py | 5 +- .../modules/test_power_state_change.py | 9 ++- .../integration_tests/modules/test_set_password.py | 6 +- .../modules/test_ssh_keys_provided.py | 4 +- .../integration_tests/modules/test_ssh_keysfile.py | 20 +++-- .../modules/test_ubuntu_advantage.py | 31 +++++--- .../modules/test_ubuntu_autoinstall.py | 4 +- .../modules/test_ubuntu_drivers.py | 3 +- .../integration_tests/modules/test_user_events.py | 20 +++-- .../integration_tests/modules/test_users_groups.py | 15 ++-- .../modules/test_version_change.py | 13 +-- tests/integration_tests/modules/test_wireguard.py | 22 +++--- tests/integration_tests/releases.py | 92 ++++++++++++++++++++++ tests/integration_tests/test_paths.py | 6 +- tests/integration_tests/test_upgrade.py | 29 ++++--- tox.ini | 11 --- 50 files changed, 460 insertions(+), 311 deletions(-) create mode 100644 tests/integration_tests/releases.py diff --git a/integration-requirements.txt b/integration-requirements.txt index 03d4fc25..85e2efcb 100644 --- a/integration-requirements.txt +++ b/integration-requirements.txt @@ -3,3 +3,4 @@ # pycloudlib==1!1 pytest +packaging diff --git a/tests/integration_tests/bugs/test_gh626.py b/tests/integration_tests/bugs/test_gh626.py index b80b677a..6df52d9c 100644 --- a/tests/integration_tests/bugs/test_gh626.py +++ b/tests/integration_tests/bugs/test_gh626.py @@ -9,6 +9,7 @@ import yaml from tests.integration_tests import random_mac_address from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM MAC_ADDRESS = random_mac_address() NETWORK_CONFIG = """\ @@ -28,8 +29,10 @@ iface eth0 inet dhcp ethernet-wol g""" -@pytest.mark.lxd_container -@pytest.mark.lxd_vm +@pytest.mark.skipif( + PLATFORM not in ["lxd_container", "lxd_vm"], + reason="Test requires custom networking provided by LXD", +) @pytest.mark.lxd_config_dict( { "user.network-config": NETWORK_CONFIG, diff --git a/tests/integration_tests/bugs/test_gh632.py b/tests/integration_tests/bugs/test_gh632.py index d83d244b..9e67fe59 100644 --- a/tests/integration_tests/bugs/test_gh632.py +++ b/tests/integration_tests/bugs/test_gh632.py @@ -6,12 +6,15 @@ no traceback if the metadata disk cannot be found. import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM from tests.integration_tests.util import verify_clean_log # With some datasource hacking, we can run this on a NoCloud instance -@pytest.mark.lxd_container -@pytest.mark.lxd_vm +@pytest.mark.skipif( + PLATFORM not in ["lxd_container", "lxd_vm"], + reason="Tested behavior is emulated using NoCloud", +) def test_datasource_rbx_no_stacktrace(client: IntegrationInstance): client.write_to_file( "/etc/cloud/cloud.cfg.d/90_dpkg.cfg", diff --git a/tests/integration_tests/bugs/test_gh668.py b/tests/integration_tests/bugs/test_gh668.py index 95edb48d..2a97c488 100644 --- a/tests/integration_tests/bugs/test_gh668.py +++ b/tests/integration_tests/bugs/test_gh668.py @@ -9,6 +9,7 @@ import pytest from tests.integration_tests import random_mac_address from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM DESTINATION_IP = "172.16.0.10" GATEWAY_IP = "10.0.0.100" @@ -32,8 +33,10 @@ ethernets: EXPECTED_ROUTE = "{} via {}".format(DESTINATION_IP, GATEWAY_IP) -@pytest.mark.lxd_container -@pytest.mark.lxd_vm +@pytest.mark.skipif( + PLATFORM not in ["lxd_container", "lxd_vm"], + reason="Test requires custom networking provided by LXD", +) @pytest.mark.lxd_config_dict( { "user.network-config": NETWORK_CONFIG, diff --git a/tests/integration_tests/bugs/test_gh671.py b/tests/integration_tests/bugs/test_gh671.py index 2d7c8118..e93bd70b 100644 --- a/tests/integration_tests/bugs/test_gh671.py +++ b/tests/integration_tests/bugs/test_gh671.py @@ -10,6 +10,7 @@ import crypt import pytest from tests.integration_tests.clouds import IntegrationCloud +from tests.integration_tests.integration_settings import PLATFORM OLD_PASSWORD = "DoIM33tTheComplexityRequirements!??" NEW_PASSWORD = "DoIM33tTheComplexityRequirementsNow!??" @@ -22,7 +23,7 @@ def _check_password(instance, unhashed_password): assert shadow_password == hashed_password -@pytest.mark.azure +@pytest.mark.skipif(PLATFORM != "azure", reason="Test is Azure specific") def test_update_default_password(setup_image, session_cloud: IntegrationCloud): os_profile = { "os_profile": { diff --git a/tests/integration_tests/bugs/test_gh868.py b/tests/integration_tests/bugs/test_gh868.py index a62e8b36..67ac9b3a 100644 --- a/tests/integration_tests/bugs/test_gh868.py +++ b/tests/integration_tests/bugs/test_gh868.py @@ -2,6 +2,7 @@ import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM from tests.integration_tests.util import verify_clean_log USERDATA = """\ @@ -15,12 +16,9 @@ chef: @pytest.mark.adhoc # Can't be regularly reaching out to chef install script -@pytest.mark.ec2 -@pytest.mark.gce -@pytest.mark.azure -@pytest.mark.oci -@pytest.mark.lxd_container -@pytest.mark.lxd_vm +@pytest.mark.skipif( + "openstack" == PLATFORM, reason="Firewall preventing openstack run" +) @pytest.mark.user_data(USERDATA) def test_chef_license(client: IntegrationInstance): log = client.read_from_file("/var/log/cloud-init.log") diff --git a/tests/integration_tests/bugs/test_lp1835584.py b/tests/integration_tests/bugs/test_lp1835584.py index 4e732446..7f110379 100644 --- a/tests/integration_tests/bugs/test_lp1835584.py +++ b/tests/integration_tests/bugs/test_lp1835584.py @@ -30,9 +30,11 @@ import re import pytest from pycloudlib.cloud import ImageType -from tests.integration_tests.clouds import ImageSpecification, IntegrationCloud +from tests.integration_tests.clouds import IntegrationCloud from tests.integration_tests.conftest import get_validated_source from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE def _check_iid_insensitive_across_kernel_upgrade( @@ -67,17 +69,15 @@ def _check_iid_insensitive_across_kernel_upgrade( assert 1 == ssh_runs, "config_ssh ran too many times {}".format(ssh_runs) -@pytest.mark.azure +@pytest.mark.skipif(PLATFORM != "azure", reason="Test is Azure specific") @pytest.mark.integration_cloud_args(image_type=ImageType.PRO_FIPS) def test_azure_kernel_upgrade_case_insensitive_uuid( session_cloud: IntegrationCloud, ): - cfg_image_spec = ImageSpecification.from_os_image() - if (cfg_image_spec.os, cfg_image_spec.release) != ("ubuntu", "bionic"): + if (CURRENT_RELEASE.os, CURRENT_RELEASE.series) != ("ubuntu", "bionic"): pytest.skip( - "Test only supports ubuntu:bionic not {0.os}:{0.release}".format( - cfg_image_spec - ) + f"Test only supports ubuntu:bionic not {CURRENT_RELEASE.os}: " + f"{CURRENT_RELEASE.series}" ) source = get_validated_source(session_cloud) if not source.installs_new_version(): @@ -87,7 +87,7 @@ def test_azure_kernel_upgrade_case_insensitive_uuid( with session_cloud.launch( launch_kwargs={ "image_id": session_cloud.cloud_instance.daily_image( - cfg_image_spec.image_id, image_type=ImageType.PRO_FIPS + CURRENT_RELEASE.image_id, image_type=ImageType.PRO_FIPS ) } ) as instance: diff --git a/tests/integration_tests/bugs/test_lp1897099.py b/tests/integration_tests/bugs/test_lp1897099.py index 1f5030ce..b0981790 100644 --- a/tests/integration_tests/bugs/test_lp1897099.py +++ b/tests/integration_tests/bugs/test_lp1897099.py @@ -7,6 +7,8 @@ https://bugs.launchpad.net/cloud-init/+bug/1897099 import pytest +from tests.integration_tests.integration_settings import PLATFORM + USER_DATA = """\ #cloud-config bootcmd: @@ -19,7 +21,9 @@ swap: @pytest.mark.user_data(USER_DATA) -@pytest.mark.no_container("Containers cannot configure swap") +@pytest.mark.skipif( + PLATFORM == "lxd_container", reason="Containers cannot configure swap" +) def test_fallocate_fallback(client): log = client.read_from_file("/var/log/cloud-init.log") assert "/swap.img" in client.execute("cat /proc/swaps") diff --git a/tests/integration_tests/bugs/test_lp1898997.py b/tests/integration_tests/bugs/test_lp1898997.py index d8ea54c3..ec92aeb6 100644 --- a/tests/integration_tests/bugs/test_lp1898997.py +++ b/tests/integration_tests/bugs/test_lp1898997.py @@ -12,6 +12,8 @@ default gateway. import pytest from tests.integration_tests import random_mac_address +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, FOCAL from tests.integration_tests.util import verify_clean_log MAC_ADDRESS = random_mac_address() @@ -44,10 +46,14 @@ version: 2 "volatile.eth0.hwaddr": MAC_ADDRESS, } ) -@pytest.mark.lxd_vm +@pytest.mark.skipif( + PLATFORM != "lxd_vm", + reason="Test requires custom networking provided by LXD", +) +@pytest.mark.skipif( + CURRENT_RELEASE < FOCAL, reason="Tested on Focal and above" +) @pytest.mark.lxd_use_exec -@pytest.mark.not_bionic -@pytest.mark.ubuntu class TestInterfaceListingWithOpenvSwitch: def test_ovs_member_interfaces_not_excluded(self, client): # We need to install openvswitch for our provided network configuration diff --git a/tests/integration_tests/bugs/test_lp1901011.py b/tests/integration_tests/bugs/test_lp1901011.py index 7de8bd77..e94caf9b 100644 --- a/tests/integration_tests/bugs/test_lp1901011.py +++ b/tests/integration_tests/bugs/test_lp1901011.py @@ -7,9 +7,10 @@ See https://github.com/canonical/cloud-init/pull/800 import pytest from tests.integration_tests.clouds import IntegrationCloud +from tests.integration_tests.integration_settings import PLATFORM -@pytest.mark.azure +@pytest.mark.skipif(PLATFORM != "azure", reason="Test is Azure specific") @pytest.mark.parametrize( "instance_type,is_ephemeral", [ diff --git a/tests/integration_tests/bugs/test_lp1910835.py b/tests/integration_tests/bugs/test_lp1910835.py index 1844594c..aa0fb75c 100644 --- a/tests/integration_tests/bugs/test_lp1910835.py +++ b/tests/integration_tests/bugs/test_lp1910835.py @@ -19,13 +19,15 @@ will match. """ import pytest +from tests.integration_tests.integration_settings import PLATFORM + USER_DATA_TMPL = """\ #cloud-config ssh_authorized_keys: - {}""" -@pytest.mark.azure +@pytest.mark.skipif(PLATFORM != "azure", reason="Test is Azure specific") def test_crlf_in_azure_metadata_ssh_keys(session_cloud, setup_image): authorized_keys_path = "/home/{}/.ssh/authorized_keys".format( session_cloud.cloud_instance.username diff --git a/tests/integration_tests/bugs/test_lp1912844.py b/tests/integration_tests/bugs/test_lp1912844.py index 55511ed2..b5aafa76 100644 --- a/tests/integration_tests/bugs/test_lp1912844.py +++ b/tests/integration_tests/bugs/test_lp1912844.py @@ -17,6 +17,7 @@ the traceback that they cause. We work around this by calling import pytest from tests.integration_tests import random_mac_address +from tests.integration_tests.integration_settings import PLATFORM MAC_ADDRESS = random_mac_address() @@ -85,7 +86,10 @@ def ovs_enabled_session_cloud(session_cloud): session_cloud.snapshot_id = old_snapshot_id -@pytest.mark.lxd_vm +@pytest.mark.skipif( + PLATFORM != "lxd_vm", + reason="Test requires custom networking provided by LXD", +) def test_get_interfaces_by_mac_doesnt_traceback(ovs_enabled_session_cloud): """Launch our OVS-enabled image and confirm the bug doesn't reproduce.""" launch_kwargs = { diff --git a/tests/integration_tests/clouds.py b/tests/integration_tests/clouds.py index 945a5fb6..404d0a08 100644 --- a/tests/integration_tests/clouds.py +++ b/tests/integration_tests/clouds.py @@ -7,7 +7,7 @@ import re import string from abc import ABC, abstractmethod from copy import deepcopy -from typing import Optional, Type +from typing import Type from uuid import UUID from pycloudlib import ( @@ -28,6 +28,7 @@ import cloudinit from cloudinit.subp import ProcessExecutionError, subp from tests.integration_tests import integration_settings from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.releases import CURRENT_RELEASE from tests.integration_tests.util import emit_dots_on_travis log = logging.getLogger("integration_testing") @@ -46,52 +47,6 @@ def _get_ubuntu_series() -> list: return out.splitlines() -class ImageSpecification: - """A specification of an image to launch for testing. - - If either of ``os`` and ``release`` are not specified, an attempt will be - made to infer the correct values for these on instantiation. - - :param image_id: - The image identifier used by the rest of the codebase to launch this - image. - :param os: - An optional string describing the operating system this image is for - (e.g. "ubuntu", "rhel", "freebsd"). - :param release: - A optional string describing the operating system release (e.g. - "focal", "8"; the exact values here will depend on the OS). - """ - - def __init__( - self, - image_id: str, - os: Optional[str] = None, - release: Optional[str] = None, - ): - if image_id in _get_ubuntu_series(): - if os is None: - os = "ubuntu" - if release is None: - release = image_id - - self.image_id = image_id - self.os = os - self.release = release - log.info( - "Detected image: image_id=%s os=%s release=%s", - self.image_id, - self.os, - self.release, - ) - - @classmethod - def from_os_image(cls): - """Return an ImageSpecification for integration_settings.OS_IMAGE.""" - parts = integration_settings.OS_IMAGE.split("::", 2) - return cls(*parts) - - class IntegrationCloud(ABC): datasource: str cloud_instance: BaseCloud @@ -127,12 +82,9 @@ class IntegrationCloud(ABC): raise NotImplementedError def _get_initial_image(self, **kwargs) -> str: - image = ImageSpecification.from_os_image() - try: - return self.cloud_instance.daily_image(image.image_id, **kwargs) - except (ValueError, IndexError) as ex: - log.debug("Exception while executing `daily_image`: %s", ex) - return image.image_id + return CURRENT_RELEASE.image_id or self.cloud_instance.daily_image( + CURRENT_RELEASE.series, **kwargs + ) def _perform_launch(self, launch_kwargs, **kwargs) -> BaseInstance: pycloudlib_instance = self.cloud_instance.launch(**launch_kwargs) @@ -398,17 +350,16 @@ class OpenstackCloud(IntegrationCloud): ) def _get_initial_image(self, **kwargs): - image = ImageSpecification.from_os_image() try: - UUID(image.image_id) + UUID(CURRENT_RELEASE.image_id) except ValueError as e: raise RuntimeError( "When using Openstack, `OS_IMAGE` MUST be specified with " "a 36-character UUID image ID. Passing in a release name is " "not valid here.\n" - "OS image id: {}".format(image.image_id) + "OS image id: {}".format(CURRENT_RELEASE.image_id) ) from e - return image.image_id + return CURRENT_RELEASE.image_id class IbmCloud(IntegrationCloud): diff --git a/tests/integration_tests/cmd/test_status.py b/tests/integration_tests/cmd/test_status.py index 2582855d..bc65bff5 100644 --- a/tests/integration_tests/cmd/test_status.py +++ b/tests/integration_tests/cmd/test_status.py @@ -3,8 +3,10 @@ from time import sleep import pytest -from tests.integration_tests.clouds import ImageSpecification, IntegrationCloud +from tests.integration_tests.clouds import IntegrationCloud from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU, JAMMY # We're implementing our own here in case cloud-init status --wait @@ -37,8 +39,11 @@ def _remove_nocloud_dir_and_reboot(client: IntegrationInstance): client.instance._wait_for_execute(old_boot_id=old_boot_id) -@pytest.mark.ubuntu -@pytest.mark.lxd_container +@pytest.mark.skipif(not IS_UBUNTU, reason="Only ever tested on Ubuntu") +@pytest.mark.skipif( + PLATFORM != "lxd_container", + reason="Test is LXD specific", +) def test_wait_when_no_datasource(session_cloud: IntegrationCloud, setup_image): """Ensure that when no datasource is found, we get status: disabled @@ -59,10 +64,7 @@ def test_wait_when_no_datasource(session_cloud: IntegrationCloud, setup_image): # No ubuntu user if cloud-init didn't run client.instance.username = "root" # Jammy and above will use LXD datasource by default - if ImageSpecification.from_os_image().release in [ - "bionic", - "focal", - ]: + if CURRENT_RELEASE < JAMMY: _remove_nocloud_dir_and_reboot(client) status_out = _wait_for_cloud_init(client).stdout.strip() assert "status: disabled" in status_out diff --git a/tests/integration_tests/conftest.py b/tests/integration_tests/conftest.py index fabeb608..a4815f42 100644 --- a/tests/integration_tests/conftest.py +++ b/tests/integration_tests/conftest.py @@ -18,7 +18,6 @@ from tests.integration_tests.clouds import ( Ec2Cloud, GceCloud, IbmCloud, - ImageSpecification, IntegrationCloud, LxdContainerCloud, LxdVmCloud, @@ -58,34 +57,10 @@ def pytest_runtest_setup(item): platform, then skip the test. If platform specific marks are not specified, then we assume the test can be run anywhere. """ - all_platforms = platforms.keys() test_marks = [mark.name for mark in item.iter_markers()] - supported_platforms = set(all_platforms).intersection(test_marks) - current_platform = integration_settings.PLATFORM - unsupported_message = "Cannot run on platform {}".format(current_platform) - if "no_container" in test_marks: - if "lxd_container" in test_marks: - raise RuntimeError( - "lxd_container and no_container marks simultaneously set " - "on test" - ) - if current_platform == "lxd_container": - pytest.skip(unsupported_message) - if supported_platforms and current_platform not in supported_platforms: - pytest.skip(unsupported_message) - - image = ImageSpecification.from_os_image() - current_os = image.os - supported_os_set = set(os_list).intersection(test_marks) - if current_os and supported_os_set and current_os not in supported_os_set: - pytest.skip("Cannot run on OS {}".format(current_os)) if "unstable" in test_marks and not integration_settings.RUN_UNSTABLE: pytest.skip("Test marked unstable. Manually remove mark to run it") - current_release = image.release - if "not_{}".format(current_release) in test_marks: - pytest.skip("Cannot run on release {}".format(current_release)) - # disable_subp_usage is defined at a higher level, but we don't # want it applied here diff --git a/tests/integration_tests/datasources/test_detect_openstack.py b/tests/integration_tests/datasources/test_detect_openstack.py index c70e9815..73246f8f 100644 --- a/tests/integration_tests/datasources/test_detect_openstack.py +++ b/tests/integration_tests/datasources/test_detect_openstack.py @@ -1,10 +1,11 @@ import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM -@pytest.mark.lxd_vm @pytest.mark.lxd_use_exec +@pytest.mark.skipif(PLATFORM != "lxd_vm", reason="Modifies grub config") def test_lxd_datasource_kernel_override(client: IntegrationInstance): """This test is twofold: it tests kernel commandline override, which also validates OpenStack Ironic requirements. OpenStack Ironic does not diff --git a/tests/integration_tests/datasources/test_ec2_ipv6.py b/tests/integration_tests/datasources/test_ec2_ipv6.py index 7bb45b40..aff7ddd2 100644 --- a/tests/integration_tests/datasources/test_ec2_ipv6.py +++ b/tests/integration_tests/datasources/test_ec2_ipv6.py @@ -3,6 +3,7 @@ import re import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM def _test_crawl(client, ip): @@ -18,7 +19,7 @@ def _test_crawl(client, ip): assert float(result[0]) < 20 -@pytest.mark.ec2 +@pytest.mark.skipif(PLATFORM != "ec2", reason="test is ec2 specific") def test_dual_stack(client: IntegrationInstance): # Drop IPv4 responses assert client.execute("iptables -I INPUT -s 169.254.169.254 -j DROP").ok diff --git a/tests/integration_tests/datasources/test_lxd_discovery.py b/tests/integration_tests/datasources/test_lxd_discovery.py index f3ca7158..4f907434 100644 --- a/tests/integration_tests/datasources/test_lxd_discovery.py +++ b/tests/integration_tests/datasources/test_lxd_discovery.py @@ -3,8 +3,9 @@ import json import pytest import yaml -from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU from tests.integration_tests.util import lxd_has_nocloud, verify_clean_log @@ -36,7 +37,7 @@ def _customize_environment(client: IntegrationInstance): "datasource_list: [LXD, NoCloud]\n", ) # This is also to ensure that NoCloud can be detected - if ImageSpecification.from_os_image().release == "jammy": + if CURRENT_RELEASE.series == "jammy": # Add nocloud-net seed files because Jammy no longer delivers NoCloud # (LP: #1958460). client.execute("mkdir -p /var/lib/cloud/seed/nocloud-net") @@ -48,9 +49,11 @@ def _customize_environment(client: IntegrationInstance): client.restart() -@pytest.mark.lxd_container -@pytest.mark.lxd_vm -@pytest.mark.ubuntu # Because netplan +@pytest.mark.skipif(not IS_UBUNTU, reason="Netplan usage") +@pytest.mark.skipif( + PLATFORM not in ["lxd_container", "lxd_vm"], + reason="Test is LXD specific", +) def test_lxd_datasource_discovery(client: IntegrationInstance): """Test that DataSourceLXD is detected instead of NoCloud.""" @@ -95,7 +98,7 @@ def test_lxd_datasource_discovery(client: IntegrationInstance): ] == sorted(list(ds_cfg.keys())) if ( client.settings.PLATFORM == "lxd_vm" - and ImageSpecification.from_os_image().release == "bionic" + and CURRENT_RELEASE.series == "bionic" ): # pycloudlib injects user.vendor_data for lxd_vm on bionic # to start the lxd-agent. diff --git a/tests/integration_tests/datasources/test_lxd_hotplug.py b/tests/integration_tests/datasources/test_lxd_hotplug.py index 81cff252..536f4e36 100644 --- a/tests/integration_tests/datasources/test_lxd_hotplug.py +++ b/tests/integration_tests/datasources/test_lxd_hotplug.py @@ -5,9 +5,10 @@ import pytest from cloudinit import safeyaml from cloudinit.subp import subp from cloudinit.util import is_true -from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.decorators import retry from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, JAMMY from tests.integration_tests.util import ( get_feature_flag_value, lxd_has_nocloud, @@ -62,8 +63,11 @@ def _prefer_lxd_datasource_over_nocloud(client: IntegrationInstance): # TODO: Once LXD adds MACs to the devices endpoint, support LXD VMs here # Currently the names are too unpredictable to be worth testing on VMs. -@pytest.mark.lxd_container @pytest.mark.user_data(USER_DATA) +@pytest.mark.skipif( + PLATFORM != "lxd_container", + reason="Test is LXD specific", +) class TestLxdHotplug: @pytest.fixture(autouse=True, scope="class") def class_teardown(self, class_client: IntegrationInstance): @@ -113,14 +117,7 @@ class TestLxdHotplug: assert "eth2" not in client.execute("ip address") pre_netplan = client.read_from_file("/etc/netplan/50-cloud-init.yaml") assert "eth2" not in pre_netplan - if ImageSpecification.from_os_image().release in [ - "bionic", - "focal", - ]: # pyright: ignore - top_key = "user" - else: - top_key = "cloud-init" - + top_key = "user" if CURRENT_RELEASE < JAMMY else "cloud-init" assert subp( [ "lxc", diff --git a/tests/integration_tests/datasources/test_network_dependency.py b/tests/integration_tests/datasources/test_network_dependency.py index bd7fe658..7b692047 100644 --- a/tests/integration_tests/datasources/test_network_dependency.py +++ b/tests/integration_tests/datasources/test_network_dependency.py @@ -1,6 +1,8 @@ import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import IS_UBUNTU def _customize_environment(client: IntegrationInstance): @@ -15,8 +17,10 @@ def _customize_environment(client: IntegrationInstance): # This test should be able to work on any cloud whose datasource specifies # a NETWORK dependency -@pytest.mark.gce -@pytest.mark.ubuntu # Because netplan +@pytest.mark.skipif(not IS_UBUNTU, reason="Netplan usage") +@pytest.mark.skipif( + PLATFORM != "gce", reason="Datasource doesn't specify a NETWORK dependency" +) def test_network_activation_disabled(client: IntegrationInstance): """Test that the network is not activated during init mode.""" _customize_environment(client) diff --git a/tests/integration_tests/datasources/test_nocloud.py b/tests/integration_tests/datasources/test_nocloud.py index d9410410..1b4c1abc 100644 --- a/tests/integration_tests/datasources/test_nocloud.py +++ b/tests/integration_tests/datasources/test_nocloud.py @@ -4,6 +4,7 @@ from pycloudlib.lxd.instance import LXDInstance from cloudinit.subp import subp from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM VENDOR_DATA = """\ #cloud-config @@ -57,10 +58,12 @@ def setup_nocloud(instance: LXDInstance): ) -# Only running on LXD container because we need NoCloud with custom setup -@pytest.mark.lxd_container @pytest.mark.lxd_setup.with_args(setup_nocloud) @pytest.mark.lxd_use_exec +@pytest.mark.skipif( + PLATFORM != "lxd_container", + reason="Requires NoCloud with custom setup", +) def test_nocloud_seedfrom_vendordata(client: IntegrationInstance): """Integration test for #570. diff --git a/tests/integration_tests/datasources/test_oci_networking.py b/tests/integration_tests/datasources/test_oci_networking.py index dc0d343b..802e8ec7 100644 --- a/tests/integration_tests/datasources/test_oci_networking.py +++ b/tests/integration_tests/datasources/test_oci_networking.py @@ -6,6 +6,7 @@ import yaml from tests.integration_tests.clouds import IntegrationCloud from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM from tests.integration_tests.util import verify_clean_log DS_CFG = """\ @@ -43,7 +44,7 @@ def extract_interface_names(network_config: dict) -> Set[str]: return set(interfaces) -@pytest.mark.oci +@pytest.mark.skipif(PLATFORM != "oci", reason="Test is OCI specific") def test_oci_networking_iscsi_instance(client: IntegrationInstance, tmpdir): customize_environment(client, tmpdir, configure_secondary_nics=False) result_net_files = client.execute("ls /run/net-*.conf") @@ -92,7 +93,7 @@ def client_with_secondary_vnic( client.instance.remove_network_interface(ip_address) -@pytest.mark.oci +@pytest.mark.skipif(PLATFORM != "oci", reason="Test is OCI specific") def test_oci_networking_iscsi_instance_secondary_vnics( client_with_secondary_vnic: IntegrationInstance, tmpdir ): @@ -142,7 +143,7 @@ def customize_netcfg( client.restart() -@pytest.mark.oci +@pytest.mark.skipif(PLATFORM != "oci", reason="Test is OCI specific") def test_oci_networking_system_cfg(client: IntegrationInstance, tmpdir): customize_netcfg(client, tmpdir) log = client.read_from_file("/var/log/cloud-init.log") diff --git a/tests/integration_tests/datasources/test_tmp_noexec.py b/tests/integration_tests/datasources/test_tmp_noexec.py index a060e20f..5aa8537d 100644 --- a/tests/integration_tests/datasources/test_tmp_noexec.py +++ b/tests/integration_tests/datasources/test_tmp_noexec.py @@ -1,6 +1,7 @@ import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM from tests.integration_tests.util import verify_clean_log @@ -14,11 +15,10 @@ def customize_client(client: IntegrationInstance): @pytest.mark.adhoc -@pytest.mark.azure -@pytest.mark.ec2 -@pytest.mark.gce -@pytest.mark.oci -@pytest.mark.openstack +@pytest.mark.skipif( + PLATFORM not in ["azure", "ec2", "gce", "oci", "openstack"], + reason=f"Test hasn't been tested on {PLATFORM}", +) def test_dhcp_tmp_noexec(client: IntegrationInstance): customize_client(client) assert ( diff --git a/tests/integration_tests/integration_settings.py b/tests/integration_tests/integration_settings.py index c4f28fcb..aca5bf91 100644 --- a/tests/integration_tests/integration_settings.py +++ b/tests/integration_tests/integration_settings.py @@ -33,9 +33,9 @@ INSTANCE_TYPE: Optional[str] = None # Determines the base image to use or generate new images from. # # This can be the name of an Ubuntu release, or in the format -# [::[::]]. If given, os and release should describe -# the image specified by image_id. (Ubuntu releases are converted to this -# format internally; in this case, to "focal::ubuntu::focal".) +# [::[::[::]]. If given, os and release should +# describe the image specified by image_id. (Ubuntu releases are converted +# to this format internally; in this case, to "None::ubuntu::focal::20.04".) OS_IMAGE = "focal" # Populate if you want to use a pre-launched instance instead of diff --git a/tests/integration_tests/modules/test_ansible.py b/tests/integration_tests/modules/test_ansible.py index 3d0f96cb..aea29b7d 100644 --- a/tests/integration_tests/modules/test_ansible.py +++ b/tests/integration_tests/modules/test_ansible.py @@ -1,5 +1,7 @@ import pytest +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, FOCAL from tests.integration_tests.util import verify_clean_log # This works by setting up a local repository and web server @@ -291,7 +293,9 @@ def test_ansible_pull_pip(client): # Ansible packaged in bionic is 2.5.1. This test relies on ansible collections, # which requires Ansible 2.9+, so no bionic. The functionality is covered # in `test_ansible_pull_pip` using pip rather than the bionic package. -@pytest.mark.not_bionic +@pytest.mark.skipif( + CURRENT_RELEASE < FOCAL, reason="Test requires Ansible 2.9+" +) @pytest.mark.user_data( USER_DATA + INSTALL_METHOD.format(package="ansible", method="distro") ) @@ -300,10 +304,14 @@ def test_ansible_pull_distro(client): @pytest.mark.user_data(ANSIBLE_CONTROL) -@pytest.mark.lxd_vm -# Not bionic because test uses pip install and version in pip is removing -# support for python version in bionic -@pytest.mark.not_bionic +@pytest.mark.skipif( + PLATFORM != "lxd_vm", + reason="Test requires starting LXD containers", +) +@pytest.mark.skipif( + CURRENT_RELEASE < FOCAL, + reason="Pip install is not supported for Ansible on release", +) def test_ansible_controller(client): log = client.read_from_file("/var/log/cloud-init.log") verify_clean_log(log) diff --git a/tests/integration_tests/modules/test_apt.py b/tests/integration_tests/modules/test_apt.py index 27ce2c5a..45c7a45a 100644 --- a/tests/integration_tests/modules/test_apt.py +++ b/tests/integration_tests/modules/test_apt.py @@ -5,8 +5,9 @@ import pytest from cloudinit import gpg from cloudinit.config import cc_apt_configure -from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU USER_DATA = """\ #cloud-config @@ -114,7 +115,7 @@ TEST_KEY = "1FF0 D853 5EF7 E719 E5C8 1B9C 083D 06FB E4D3 04DF" TEST_SIGNED_BY_KEY = "A2EB 2DEC 0BD7 519B 7B38 BE38 376A 290E C806 8B11" -@pytest.mark.ubuntu +@pytest.mark.skipif(not IS_UBUNTU, reason="Apt usage") @pytest.mark.user_data(USER_DATA) class TestApt: def get_keys(self, class_client: IntegrationInstance): @@ -165,10 +166,11 @@ class TestApt: Ported from tests/cloud_tests/testcases/modules/apt_configure_sources_ppa.py """ - release = ImageSpecification.from_os_image().release ppa_path_contents = class_client.read_from_file( "/etc/apt/sources.list.d/" - "simplestreams-dev-ubuntu-trunk-{}.list".format(release) + "simplestreams-dev-ubuntu-trunk-{}.list".format( + CURRENT_RELEASE.series + ) ) assert ( "://ppa.launchpad.net/simplestreams-dev/trunk/ubuntu" @@ -181,11 +183,10 @@ class TestApt: def test_signed_by(self, class_client: IntegrationInstance): """Test the apt signed-by functionality.""" - release = ImageSpecification.from_os_image().release source = ( "deb [signed-by=/etc/apt/cloud-init.gpg.d/test_signed_by.gpg] " "http://ppa.launchpad.net/juju/stable/ubuntu" - " {} main".format(release) + " {} main".format(CURRENT_RELEASE.series) ) path_contents = class_client.read_from_file( "/etc/apt/sources.list.d/test_signed_by.list" @@ -251,27 +252,27 @@ class TestApt: def test_sources_write(self, class_client: IntegrationInstance): """Test overwrite or append to sources file""" - release = ImageSpecification.from_os_image().release test_write_content = class_client.read_from_file( "/etc/apt/sources.list.d/test_write.list" ) expected_contents = ( "deb [signed-by=/etc/apt/cloud-init.gpg.d/test_write.gpg] " - f"http://ppa.launchpad.net/juju/devel/ubuntu {release} main" + "http://ppa.launchpad.net/juju/devel/ubuntu " + f"{CURRENT_RELEASE.series} main" ) assert expected_contents.strip() == test_write_content.strip() def test_sources_append(self, class_client: IntegrationInstance): - release = ImageSpecification.from_os_image().release + series = CURRENT_RELEASE.series test_append_content = class_client.read_from_file( "/etc/apt/sources.list.d/test_append.list" ) expected_contents = ( "deb [signed-by=/etc/apt/cloud-init.gpg.d/test_append.gpg] " - f"http://ppa.launchpad.net/juju/stable/ubuntu {release} main\n" + f"http://ppa.launchpad.net/juju/stable/ubuntu {series} main\n" "deb [signed-by=/etc/apt/cloud-init.gpg.d/test_append.gpg] " - f"http://ppa.launchpad.net/juju/devel/ubuntu {release} main" + f"http://ppa.launchpad.net/juju/devel/ubuntu {series} main" ) assert expected_contents.strip() == test_append_content.strip() @@ -290,10 +291,12 @@ apt: DEFAULT_DATA = _DEFAULT_DATA.format(uri="") -@pytest.mark.ubuntu +@pytest.mark.skipif(not IS_UBUNTU, reason="Apt usage") @pytest.mark.user_data(DEFAULT_DATA) class TestDefaults: - @pytest.mark.openstack + @pytest.mark.skipif( + PLATFORM != "openstack", reason="Test is Openstack specific" + ) def test_primary_on_openstack(self, class_client: IntegrationInstance): """Test apt default primary source on openstack. @@ -312,12 +315,12 @@ class TestDefaults: sources_list = class_client.read_from_file("/etc/apt/sources.list") # 3 lines from main, universe, and multiverse - release = ImageSpecification.from_os_image().release - sec_url = f"deb http://security.ubuntu.com/ubuntu {release}-security" + series = CURRENT_RELEASE.series + sec_url = f"deb http://security.ubuntu.com/ubuntu {series}-security" if class_client.settings.PLATFORM == "azure": sec_url = ( f"deb http://azure.archive.ubuntu.com/ubuntu/" - f" {release}-security" + f" {series}-security" ) sec_src_url = sec_url.replace("deb ", "# deb-src ") assert 3 == sources_list.count(sec_url) @@ -354,7 +357,7 @@ apt_pipelining: false """ -@pytest.mark.ubuntu +@pytest.mark.skipif(not IS_UBUNTU, reason="Apt usage") @pytest.mark.user_data(DISABLED_DATA) class TestDisabled: def test_disable_suites(self, class_client: IntegrationInstance): @@ -390,7 +393,7 @@ apt: """ -@pytest.mark.ubuntu +@pytest.mark.skipif(not IS_UBUNTU, reason="Apt usage") @pytest.mark.user_data(APT_PROXY_DATA) def test_apt_proxy(client: IntegrationInstance): """Test the apt proxy data gets written correctly.""" diff --git a/tests/integration_tests/modules/test_ca_certs.py b/tests/integration_tests/modules/test_ca_certs.py index 2baedda9..65f8f4d7 100644 --- a/tests/integration_tests/modules/test_ca_certs.py +++ b/tests/integration_tests/modules/test_ca_certs.py @@ -11,6 +11,7 @@ import os.path import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.releases import IS_UBUNTU from tests.integration_tests.util import get_inactive_modules, verify_clean_log USER_DATA = """\ @@ -57,7 +58,9 @@ ca_certs: """ -@pytest.mark.ubuntu +@pytest.mark.skipif( + not IS_UBUNTU, reason="CA cert functionality is distro specific" +) @pytest.mark.user_data(USER_DATA) class TestCaCerts: def test_certs_updated(self, class_client: IntegrationInstance): diff --git a/tests/integration_tests/modules/test_combined.py b/tests/integration_tests/modules/test_combined.py index 8481b454..3c238e1f 100644 --- a/tests/integration_tests/modules/test_combined.py +++ b/tests/integration_tests/modules/test_combined.py @@ -16,9 +16,10 @@ import pytest import cloudinit.config from cloudinit.util import is_true -from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.decorators import retry from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU from tests.integration_tests.util import ( get_feature_flag_value, get_inactive_modules, @@ -83,7 +84,7 @@ timezone: US/Aleutian @pytest.mark.ci @pytest.mark.user_data(USER_DATA) class TestCombined: - @pytest.mark.ubuntu # Because netplan + @pytest.mark.skipif(not IS_UBUNTU, reason="Uses netplan") def test_netplan_permissions(self, class_client: IntegrationInstance): """ Test that netplan config file is generated with proper permissions @@ -303,19 +304,20 @@ class TestCombined: assert data["base64_encoded_keys"] == [] assert data["merged_cfg"] == "redacted for non-root user" - image_spec = ImageSpecification.from_os_image() - image_spec = ImageSpecification.from_os_image() - assert data["sys_info"]["dist"][0] == image_spec.os + assert data["sys_info"]["dist"][0] == CURRENT_RELEASE.os v1_data = data["v1"] assert re.match(r"\d\.\d+\.\d+-\d+", v1_data["kernel_release"]) - assert v1_data["variant"] == image_spec.os - assert v1_data["distro"] == image_spec.os - assert v1_data["distro_release"] == image_spec.release + assert v1_data["variant"] == CURRENT_RELEASE.os + assert v1_data["distro"] == CURRENT_RELEASE.os + assert v1_data["distro_release"] == CURRENT_RELEASE.series assert v1_data["machine"] == "x86_64" assert re.match(r"3.\d+\.\d+", v1_data["python_version"]) - @pytest.mark.lxd_container + @pytest.mark.skipif( + PLATFORM != "lxd_container", + reason="Test is LXD container specific", + ) def test_instance_json_lxd(self, class_client: IntegrationInstance): client = class_client instance_json_file = client.read_from_file( @@ -351,7 +353,7 @@ class TestCombined: assert v1_data["local_hostname"] == client.instance.name assert v1_data["region"] is None - @pytest.mark.lxd_vm + @pytest.mark.skipif(PLATFORM != "lxd_vm", reason="Test is LXD VM specific") def test_instance_json_lxd_vm(self, class_client: IntegrationInstance): client = class_client instance_json_file = client.read_from_file( @@ -396,7 +398,7 @@ class TestCombined: assert v1_data["local_hostname"] == client.instance.name assert v1_data["region"] is None - @pytest.mark.ec2 + @pytest.mark.skipif(PLATFORM != "ec2", reason="Test is ec2 specific") def test_instance_json_ec2(self, class_client: IntegrationInstance): client = class_client instance_json_file = client.read_from_file( @@ -419,7 +421,7 @@ class TestCombined: assert v1_data["local_hostname"].startswith("ip-") assert v1_data["region"] == client.cloud.cloud_instance.region - @pytest.mark.gce + @pytest.mark.skipif(PLATFORM != "gce", reason="Test is GCE specific") def test_instance_json_gce(self, class_client: IntegrationInstance): client = class_client instance_json_file = client.read_from_file( @@ -438,10 +440,13 @@ class TestCombined: assert v1_data["instance_id"] == client.instance.instance_id assert v1_data["local_hostname"] == client.instance.name - @pytest.mark.lxd_container - @pytest.mark.azure - @pytest.mark.gce - @pytest.mark.ec2 + @pytest.mark.skipif( + PLATFORM not in ["lxd_container", "azure", "gce", "ec2"], + reason=( + f"Test was written for {PLATFORM} but can likely run on " + "other platforms." + ), + ) def test_instance_cloud_id_across_reboot( self, class_client: IntegrationInstance ): diff --git a/tests/integration_tests/modules/test_disk_setup.py b/tests/integration_tests/modules/test_disk_setup.py index 1aa22ef0..084f03f5 100644 --- a/tests/integration_tests/modules/test_disk_setup.py +++ b/tests/integration_tests/modules/test_disk_setup.py @@ -7,6 +7,8 @@ from pycloudlib.lxd.instance import LXDInstance from cloudinit.subp import subp from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, FOCAL, IS_UBUNTU from tests.integration_tests.util import verify_clean_log DISK_PATH = "/tmp/test_disk_setup_{}".format(uuid4()) @@ -52,8 +54,10 @@ mounts: @pytest.mark.user_data(ALIAS_USERDATA) @pytest.mark.lxd_setup.with_args(setup_and_mount_lxd_disk) -@pytest.mark.ubuntu -@pytest.mark.lxd_vm +@pytest.mark.skipif(not IS_UBUNTU, reason="Only ever tested on Ubuntu") +@pytest.mark.skipif( + PLATFORM != "lxd_vm", reason="Test requires additional mounted device" +) class TestDeviceAliases: """Test devices aliases work on disk setup/mount""" @@ -124,8 +128,10 @@ mounts: @pytest.mark.user_data(PARTPROBE_USERDATA) @pytest.mark.lxd_setup.with_args(setup_and_mount_lxd_disk) -@pytest.mark.ubuntu -@pytest.mark.lxd_vm +@pytest.mark.skipif(not IS_UBUNTU, reason="Only ever tested on Ubuntu") +@pytest.mark.skipif( + PLATFORM != "lxd_vm", reason="Test requires additional mounted device" +) class TestPartProbeAvailability: """Test disk setup works with partprobe @@ -149,9 +155,10 @@ class TestPartProbeAvailability: assert sdb["children"][0]["mountpoints"] == ["/mnt1"] assert sdb["children"][1]["mountpoints"] == ["/mnt2"] - # Not bionic because the LXD agent gets in the way of us - # changing the userdata - @pytest.mark.not_bionic + @pytest.mark.skipif( + CURRENT_RELEASE < FOCAL, + reason="LXD agent gets in the way of changing userdata", + ) def test_disk_setup_when_mounted( self, create_disk, client: IntegrationInstance ): diff --git a/tests/integration_tests/modules/test_growpart.py b/tests/integration_tests/modules/test_growpart.py index 67251817..b42c016c 100644 --- a/tests/integration_tests/modules/test_growpart.py +++ b/tests/integration_tests/modules/test_growpart.py @@ -8,6 +8,8 @@ from pycloudlib.lxd.instance import LXDInstance from cloudinit.subp import subp from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import IS_UBUNTU DISK_PATH = "/tmp/test_disk_setup_{}".format(uuid4()) @@ -48,8 +50,10 @@ runcmd: @pytest.mark.user_data(ALIAS_USERDATA) @pytest.mark.lxd_setup.with_args(setup_and_mount_lxd_disk) -@pytest.mark.ubuntu -@pytest.mark.lxd_vm +@pytest.mark.skipif(not IS_UBUNTU, reason="Only ever tested on Ubuntu") +@pytest.mark.skipif( + PLATFORM != "lxd_vm", reason="Test requires additional mounted device" +) class TestGrowPart: """Test growpart""" diff --git a/tests/integration_tests/modules/test_hotplug.py b/tests/integration_tests/modules/test_hotplug.py index 0bad761e..120715e4 100644 --- a/tests/integration_tests/modules/test_hotplug.py +++ b/tests/integration_tests/modules/test_hotplug.py @@ -5,6 +5,8 @@ import pytest import yaml from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, FOCAL USER_DATA = """\ #cloud-config @@ -40,11 +42,17 @@ def _get_ip_addr(client): return ips -@pytest.mark.openstack -# On Bionic, we traceback when attempting to detect the hotplugged -# device in the updated metadata. This is because Bionic is specifically -# configured not to provide network metadata. -@pytest.mark.not_bionic +@pytest.mark.skipif( + PLATFORM != "openstack", + reason=( + f"Test was written for {PLATFORM} but can likely run on " + "other platforms." + ), +) +@pytest.mark.skipif( + CURRENT_RELEASE < FOCAL, + reason="Openstack network metadata support was added in focal.", +) @pytest.mark.user_data(USER_DATA) def test_hotplug_add_remove(client: IntegrationInstance): ips_before = _get_ip_addr(client) @@ -85,7 +93,13 @@ def test_hotplug_add_remove(client: IntegrationInstance): ) -@pytest.mark.openstack +@pytest.mark.skipif( + PLATFORM != "openstack", + reason=( + f"Test was written for {PLATFORM} but can likely run on " + "other platforms." + ), +) def test_no_hotplug_in_userdata(client: IntegrationInstance): ips_before = _get_ip_addr(client) log = client.read_from_file("/var/log/cloud-init.log") diff --git a/tests/integration_tests/modules/test_keys_to_console.py b/tests/integration_tests/modules/test_keys_to_console.py index 5e2b3645..b10cbc60 100644 --- a/tests/integration_tests/modules/test_keys_to_console.py +++ b/tests/integration_tests/modules/test_keys_to_console.py @@ -6,6 +6,7 @@ import pytest from tests.integration_tests.decorators import retry from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM from tests.integration_tests.util import get_console_log BLACKLIST_USER_DATA = """\ @@ -88,11 +89,13 @@ class TestKeysToConsoleDisabled: @pytest.mark.user_data(ENABLE_KEYS_TO_CONSOLE_USER_DATA) @retry(tries=30, delay=1) -@pytest.mark.ec2 -@pytest.mark.lxd_container -@pytest.mark.oci -@pytest.mark.openstack -# No Azure because no console log on Azure +@pytest.mark.skipif( + PLATFORM not in ["ec2", "lxd_container", "oci", "openstack"], + reason=( + "No Azure because no console log on Azure. " + "Other platforms need testing." + ), +) def test_duplicate_messaging_console_log(client: IntegrationInstance): """Test that output can be enabled disabled.""" assert ( diff --git a/tests/integration_tests/modules/test_lxd.py b/tests/integration_tests/modules/test_lxd.py index f84cdff6..40941ab4 100644 --- a/tests/integration_tests/modules/test_lxd.py +++ b/tests/integration_tests/modules/test_lxd.py @@ -8,8 +8,10 @@ import warnings import pytest import yaml -from tests.integration_tests.clouds import ImageSpecification, IntegrationCloud +from tests.integration_tests.clouds import IntegrationCloud from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, FOCAL from tests.integration_tests.util import verify_clean_log BRIDGE_USER_DATA = """\ @@ -149,7 +151,9 @@ lxd: """ -@pytest.mark.no_container +@pytest.mark.skipif( + PLATFORM == "lxd_container", reason="Containers cannot run LXD" +) @pytest.mark.user_data(BRIDGE_USER_DATA) class TestLxdBridge: @pytest.mark.parametrize("binary_name", ["lxc", "lxd"]) @@ -202,7 +206,7 @@ def validate_preseed_storage_pools(client, preseed_cfg): def validate_preseed_projects(client: IntegrationInstance, preseed_cfg): # Support for projects by lxd init --preseed was added in lxd 4.12 # https://discuss.linuxcontainers.org/t/lxd-4-12-has-been-released/10424#projects-now-supported-by-lxd-init-dump-and-preseed-9 - if ImageSpecification.from_os_image().release in ("bionic", "focal"): + if CURRENT_RELEASE.series in ("bionic", "focal"): return for src_project in preseed_cfg.get("projects", []): proj_name = src_project["name"] @@ -226,17 +230,23 @@ def validate_preseed_projects(client: IntegrationInstance, preseed_cfg): assert project == src_project -@pytest.mark.no_container +@pytest.mark.skipif( + PLATFORM == "lxd_container", reason="Containers cannot manipulate storage" +) @pytest.mark.user_data(STORAGE_USER_DATA.format("btrfs")) def test_storage_btrfs(client): validate_storage(client, "btrfs-progs", "mkfs.btrfs") -@pytest.mark.no_container -@pytest.mark.not_bionic +@pytest.mark.skipif( + PLATFORM == "lxd_container", reason="Containers cannot manipulate LXD" +) +@pytest.mark.skipif( + CURRENT_RELEASE < FOCAL, reason="tested on Focal and later" +) def test_storage_preseed_btrfs(setup_image, session_cloud: IntegrationCloud): - cfg_image_spec = ImageSpecification.from_os_image() - if cfg_image_spec.release in ("bionic",): + # TODO: If test is marked as not bionic, why is there a bionic section? + if CURRENT_RELEASE.series in ("bionic",): nictype = "nictype: bridged" parent = "parent: lxdbr0" network = "" @@ -256,7 +266,9 @@ def test_storage_preseed_btrfs(setup_image, session_cloud: IntegrationCloud): validate_preseed_projects(client, preseed_cfg) -@pytest.mark.no_container +@pytest.mark.skipif( + PLATFORM == "lxd_container", reason="Containers cannot manipulate LVM" +) @pytest.mark.user_data(STORAGE_USER_DATA.format("lvm")) def test_storage_lvm(client): log = client.read_from_file("/var/log/cloud-init.log") @@ -281,17 +293,23 @@ def test_basic_preseed(client): validate_preseed_projects(client, preseed_cfg) -@pytest.mark.no_container +@pytest.mark.skipif( + PLATFORM == "lxd_container", reason="Containers cannot manipulate ZFS" +) @pytest.mark.user_data(STORAGE_USER_DATA.format("zfs")) def test_storage_zfs(client): validate_storage(client, "zfsutils-linux", "zpool") -@pytest.mark.no_container -@pytest.mark.not_bionic +@pytest.mark.skipif( + PLATFORM == "lxd_container", reason="Containers cannot manipulate LXD" +) +@pytest.mark.skipif( + CURRENT_RELEASE < FOCAL, reason="Tested on focal and later" +) def test_storage_preseed_zfs(setup_image, session_cloud: IntegrationCloud): - cfg_image_spec = ImageSpecification.from_os_image() - if cfg_image_spec.release in ("bionic",): + # TODO: If test is marked as not bionic, why is there a bionic section? + if CURRENT_RELEASE.series in ("bionic",): nictype = "nictype: bridged" parent = "parent: lxdbr0" network = "" diff --git a/tests/integration_tests/modules/test_package_update_upgrade_install.py b/tests/integration_tests/modules/test_package_update_upgrade_install.py index d668d81c..c54365b8 100644 --- a/tests/integration_tests/modules/test_package_update_upgrade_install.py +++ b/tests/integration_tests/modules/test_package_update_upgrade_install.py @@ -16,6 +16,8 @@ import re import pytest +from tests.integration_tests.releases import IS_UBUNTU + USER_DATA = """\ #cloud-config packages: @@ -26,7 +28,7 @@ package_upgrade: true """ -@pytest.mark.ubuntu +@pytest.mark.skipif(not IS_UBUNTU, reason="Uses Apt") @pytest.mark.user_data(USER_DATA) class TestPackageUpdateUpgradeInstall: def assert_package_installed(self, pkg_out, name, version=None): diff --git a/tests/integration_tests/modules/test_persistence.py b/tests/integration_tests/modules/test_persistence.py index 9979cc06..67f48284 100644 --- a/tests/integration_tests/modules/test_persistence.py +++ b/tests/integration_tests/modules/test_persistence.py @@ -5,6 +5,7 @@ from pathlib import Path import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM from tests.integration_tests.util import ( ASSETS_DIR, verify_ordered_items_in_text, @@ -14,7 +15,9 @@ PICKLE_PATH = Path("/var/lib/cloud/instance/obj.pkl") TEST_PICKLE = ASSETS_DIR / "trusty_with_mime.pkl" -@pytest.mark.lxd_container +@pytest.mark.skipif( + PLATFORM != "lxd_container", reason=f"Not tested on {PLATFORM}" +) def test_log_message_on_missing_version_file(client: IntegrationInstance): client.push_file(TEST_PICKLE, PICKLE_PATH) client.restart() diff --git a/tests/integration_tests/modules/test_power_state_change.py b/tests/integration_tests/modules/test_power_state_change.py index 5cd19764..dd5bd478 100644 --- a/tests/integration_tests/modules/test_power_state_change.py +++ b/tests/integration_tests/modules/test_power_state_change.py @@ -9,6 +9,8 @@ import pytest from tests.integration_tests.clouds import IntegrationCloud from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import IS_UBUNTU from tests.integration_tests.util import verify_ordered_items_in_text USER_DATA = """\ @@ -51,8 +53,11 @@ def _can_connect(instance): # run anywhere, I can only get it to run in an lxd container, and even then # occasionally some timing issues will crop up. @pytest.mark.unstable -@pytest.mark.ubuntu -@pytest.mark.lxd_container +@pytest.mark.skipif(not IS_UBUNTU, reason="Only ever tested on Ubuntu") +@pytest.mark.skipif( + PLATFORM != "lxd_container", + reason="Test is unstable but most stable on lxd containers", +) class TestPowerChange: @pytest.mark.parametrize( "mode,delay,timeout,expected", diff --git a/tests/integration_tests/modules/test_set_password.py b/tests/integration_tests/modules/test_set_password.py index 765dd30c..48fe536c 100644 --- a/tests/integration_tests/modules/test_set_password.py +++ b/tests/integration_tests/modules/test_set_password.py @@ -11,8 +11,8 @@ only specify one user-data per instance. import pytest import yaml -from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.decorators import retry +from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU from tests.integration_tests.util import get_console_log COMMON_USER_DATA = """\ @@ -182,7 +182,7 @@ class Mixin: def test_sshd_config_file(self, class_client): """Test that SSH config is written in the correct file.""" - if ImageSpecification.from_os_image().release in {"bionic"}: + if CURRENT_RELEASE.series == "bionic": sshd_file_target = "/etc/ssh/sshd_config" else: sshd_file_target = "/etc/ssh/sshd_config.d/50-cloud-init.conf" @@ -191,7 +191,7 @@ class Mixin: # We look for the exact line match, to avoid a commented line matching assert "PasswordAuthentication yes" in sshd_config.splitlines() - @pytest.mark.ubuntu + @pytest.mark.skipif(not IS_UBUNTU, reason="Use of systemctl") def test_check_ssh_service(self, class_client): """Ensure we check the sshd status because we modified the config""" log = class_client.read_from_file("/var/log/cloud-init.log") diff --git a/tests/integration_tests/modules/test_ssh_keys_provided.py b/tests/integration_tests/modules/test_ssh_keys_provided.py index b6069376..576b78eb 100644 --- a/tests/integration_tests/modules/test_ssh_keys_provided.py +++ b/tests/integration_tests/modules/test_ssh_keys_provided.py @@ -9,7 +9,7 @@ system. import pytest -from tests.integration_tests.clouds import ImageSpecification +from tests.integration_tests.releases import CURRENT_RELEASE USER_DATA = """\ #cloud-config @@ -143,7 +143,7 @@ class TestSshKeysProvided: "HostCertificate /etc/ssh/ssh_host_rsa_key-cert.pub", "HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub", ) - if ImageSpecification.from_os_image().release in {"bionic"}: + if CURRENT_RELEASE.series == "bionic": sshd_config_path = "/etc/ssh/sshd_config" else: sshd_config_path = "/etc/ssh/sshd_config.d/50-cloud-init.conf" diff --git a/tests/integration_tests/modules/test_ssh_keysfile.py b/tests/integration_tests/modules/test_ssh_keysfile.py index 8330a1ce..628ad91f 100644 --- a/tests/integration_tests/modules/test_ssh_keysfile.py +++ b/tests/integration_tests/modules/test_ssh_keysfile.py @@ -4,8 +4,8 @@ import paramiko import pytest from paramiko.ssh_exception import SSHException -from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU from tests.integration_tests.util import get_test_rsa_keypair TEST_USER1_KEYS = get_test_rsa_keypair("test1") @@ -91,7 +91,7 @@ def common_verify(client, expected_keys): home_dir = "/home/{}".format(user) # Home permissions aren't consistent between releases. On ubuntu # this can change to 750 once focal is unsupported. - if ImageSpecification.from_os_image().release in ("bionic", "focal"): + if CURRENT_RELEASE.series in ("bionic", "focal"): home_perms = "755" else: home_perms = "750" @@ -125,7 +125,9 @@ def common_verify(client, expected_keys): DEFAULT_KEYS_USERDATA = _USERDATA.format(bootcmd='""') -@pytest.mark.ubuntu +@pytest.mark.skipif( + not IS_UBUNTU, reason="Tests permissions specific to Ubuntu releases" +) @pytest.mark.user_data(DEFAULT_KEYS_USERDATA) def test_authorized_keys_default(client: IntegrationInstance): expected_keys = [ @@ -154,7 +156,9 @@ AUTHORIZED_KEYS2_USERDATA = _USERDATA.format( ) -@pytest.mark.ubuntu +@pytest.mark.skipif( + not IS_UBUNTU, reason="Tests permissions specific to Ubuntu releases" +) @pytest.mark.user_data(AUTHORIZED_KEYS2_USERDATA) def test_authorized_keys2(client: IntegrationInstance): expected_keys = [ @@ -183,7 +187,9 @@ NESTED_KEYS_USERDATA = _USERDATA.format( ) -@pytest.mark.ubuntu +@pytest.mark.skipif( + not IS_UBUNTU, reason="Tests permissions specific to Ubuntu releases" +) @pytest.mark.user_data(NESTED_KEYS_USERDATA) def test_nested_keys(client: IntegrationInstance): expected_keys = [ @@ -204,7 +210,9 @@ EXTERNAL_KEYS_USERDATA = _USERDATA.format( ) -@pytest.mark.ubuntu +@pytest.mark.skipif( + not IS_UBUNTU, reason="Tests permissions specific to Ubuntu releases" +) @pytest.mark.user_data(EXTERNAL_KEYS_USERDATA) def test_external_keys(client: IntegrationInstance): expected_keys = [ diff --git a/tests/integration_tests/modules/test_ubuntu_advantage.py b/tests/integration_tests/modules/test_ubuntu_advantage.py index 547ec9e7..0b3ed2d7 100644 --- a/tests/integration_tests/modules/test_ubuntu_advantage.py +++ b/tests/integration_tests/modules/test_ubuntu_advantage.py @@ -5,12 +5,20 @@ import os import pytest from pycloudlib.cloud import ImageType -from tests.integration_tests.clouds import ImageSpecification, IntegrationCloud +from tests.integration_tests.clouds import IntegrationCloud from tests.integration_tests.conftest import get_validated_source from tests.integration_tests.instances import ( CloudInitSource, IntegrationInstance, ) +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import ( + BIONIC, + CURRENT_RELEASE, + FOCAL, + IS_UBUNTU, + JAMMY, +) from tests.integration_tests.util import verify_clean_log LOG = logging.getLogger("integration_testing.test_ubuntu_advantage") @@ -109,7 +117,7 @@ def get_services_status(client: IntegrationInstance) -> dict: @pytest.mark.adhoc -@pytest.mark.ubuntu +@pytest.mark.skipif(not IS_UBUNTU, reason="Test is Ubuntu specific") @pytest.mark.skipif( not CLOUD_INIT_UA_TOKEN, reason="CLOUD_INIT_UA_TOKEN env var not provided" ) @@ -136,12 +144,11 @@ class TestUbuntuAdvantage: def maybe_install_cloud_init(session_cloud: IntegrationCloud): - cfg_image_spec = ImageSpecification.from_os_image() source = get_validated_source(session_cloud) launch_kwargs = { "image_id": session_cloud.cloud_instance.daily_image( - cfg_image_spec.image_id, image_type=ImageType.PRO + CURRENT_RELEASE.image_id, image_type=ImageType.PRO ) } @@ -192,16 +199,16 @@ def maybe_install_cloud_init(session_cloud: IntegrationCloud): return {"image_id": session_cloud.snapshot_id} -@pytest.mark.azure -@pytest.mark.ec2 -@pytest.mark.gce -@pytest.mark.ubuntu +@pytest.mark.skipif( + not all([IS_UBUNTU, CURRENT_RELEASE in [BIONIC, FOCAL, JAMMY]]), + reason="Test runs on Ubuntu LTS releases only", +) +@pytest.mark.skipif( + PLATFORM not in ["azure", "ec2", "gce"], + reason=f"Pro isn't offered on {PLATFORM}.", +) class TestUbuntuAdvantagePro: def test_custom_services(self, session_cloud: IntegrationCloud): - release = ImageSpecification.from_os_image().release - if release not in {"bionic", "focal", "jammy"}: - pytest.skip(f"Cannot run on non LTS release: {release}") - launch_kwargs = maybe_install_cloud_init(session_cloud) with session_cloud.launch( user_data=AUTO_ATTACH_CUSTOM_SERVICES, diff --git a/tests/integration_tests/modules/test_ubuntu_autoinstall.py b/tests/integration_tests/modules/test_ubuntu_autoinstall.py index d340afc5..ad077977 100644 --- a/tests/integration_tests/modules/test_ubuntu_autoinstall.py +++ b/tests/integration_tests/modules/test_ubuntu_autoinstall.py @@ -2,6 +2,8 @@ import pytest +from tests.integration_tests.releases import IS_UBUNTU + USER_DATA = """\ #cloud-config autoinstall: @@ -16,7 +18,7 @@ snap: LOG_MSG = "Valid autoinstall schema. Config will be processed by subiquity" -@pytest.mark.ubuntu +@pytest.mark.skipif(not IS_UBUNTU, reason="Test is Ubuntu specific") @pytest.mark.user_data(USER_DATA) class TestUbuntuAutoinstall: def test_autoinstall_schema_valid_when_snap_present(self, class_client): diff --git a/tests/integration_tests/modules/test_ubuntu_drivers.py b/tests/integration_tests/modules/test_ubuntu_drivers.py index 4fbfba3c..66649c17 100644 --- a/tests/integration_tests/modules/test_ubuntu_drivers.py +++ b/tests/integration_tests/modules/test_ubuntu_drivers.py @@ -3,6 +3,7 @@ import re import pytest from tests.integration_tests.clouds import IntegrationCloud +from tests.integration_tests.integration_settings import PLATFORM from tests.integration_tests.util import verify_clean_log USER_DATA = """\ @@ -16,7 +17,7 @@ drivers: @pytest.mark.adhoc # Expensive instance type -@pytest.mark.oci +@pytest.mark.skipif(PLATFORM != "oci", reason="Test is OCI specific") def test_ubuntu_drivers_installed(session_cloud: IntegrationCloud): with session_cloud.launch( launch_kwargs={"instance_type": "VM.GPU2.1"}, user_data=USER_DATA diff --git a/tests/integration_tests/modules/test_user_events.py b/tests/integration_tests/modules/test_user_events.py index e4a4241f..79d88022 100644 --- a/tests/integration_tests/modules/test_user_events.py +++ b/tests/integration_tests/modules/test_user_events.py @@ -9,6 +9,7 @@ import pytest import yaml from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM def _add_dummy_bridge_to_netplan(client: IntegrationInstance): @@ -26,12 +27,11 @@ def _add_dummy_bridge_to_netplan(client: IntegrationInstance): client.write_to_file("/etc/netplan/50-cloud-init.yaml", dumped_netplan) -@pytest.mark.lxd_container -@pytest.mark.lxd_vm -@pytest.mark.ec2 -@pytest.mark.gce -@pytest.mark.oci -@pytest.mark.openstack +@pytest.mark.skipif( + PLATFORM + not in ["lxd_container", "lxd_vm", "ec2", "gce", "oci", "openstack"], + reason="Default boot events testing is datasource specific", +) def test_boot_event_disabled_by_default(client: IntegrationInstance): log = client.read_from_file("/var/log/cloud-init.log") if "network config is disabled" in log: @@ -92,7 +92,13 @@ def _test_network_config_applied_on_reboot(client: IntegrationInstance): assert "dummy0" not in client.execute("ls /sys/class/net") -@pytest.mark.azure +@pytest.mark.skipif( + PLATFORM != "azure", + reason=( + f"{PLATFORM} doesn't support updates every boot event by default " + "(or hasn't been testing for it)." + ), +) def test_boot_event_enabled_by_default(client: IntegrationInstance): _test_network_config_applied_on_reboot(client) diff --git a/tests/integration_tests/modules/test_users_groups.py b/tests/integration_tests/modules/test_users_groups.py index 91eca345..cc6a4bfb 100644 --- a/tests/integration_tests/modules/test_users_groups.py +++ b/tests/integration_tests/modules/test_users_groups.py @@ -8,8 +8,8 @@ import re import pytest -from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.releases import CURRENT_RELEASE, IS_UBUNTU, JAMMY from tests.integration_tests.util import verify_clean_log USER_DATA = """\ @@ -56,7 +56,7 @@ class TestUsersGroups: confirms that they have been configured correctly in the system under test. """ - @pytest.mark.ubuntu + @pytest.mark.skipif(not IS_UBUNTU, reason="Test assumes 'ubuntu' user") @pytest.mark.parametrize( "getent_args,regex", [ @@ -108,6 +108,10 @@ class TestUsersGroups: @pytest.mark.user_data(USER_DATA) +@pytest.mark.skipif( + CURRENT_RELEASE < JAMMY, + reason="Requires version of sudo not available in older releases", +) def test_sudoers_includedir(client: IntegrationInstance): """Ensure we don't add additional #includedir to sudoers. @@ -117,13 +121,6 @@ def test_sudoers_includedir(client: IntegrationInstance): https://github.com/canonical/cloud-init/pull/783 """ - if ImageSpecification.from_os_image().release in [ - "bionic", - "focal", - ]: - raise pytest.skip( - "Test requires version of sudo installed on groovy and later" - ) client.execute("sed -i 's/#include/@include/g' /etc/sudoers") sudoers = client.read_from_file("/etc/sudoers") diff --git a/tests/integration_tests/modules/test_version_change.py b/tests/integration_tests/modules/test_version_change.py index 3168cd60..9df436b8 100644 --- a/tests/integration_tests/modules/test_version_change.py +++ b/tests/integration_tests/modules/test_version_change.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM from tests.integration_tests.util import ASSETS_DIR, verify_clean_log PICKLE_PATH = Path("/var/lib/cloud/instance/obj.pkl") @@ -41,12 +42,12 @@ def test_reboot_without_version_change(client: IntegrationInstance): ) -@pytest.mark.ec2 -@pytest.mark.gce -@pytest.mark.oci -@pytest.mark.openstack -@pytest.mark.lxd_container -@pytest.mark.lxd_vm +@pytest.mark.skipif( + PLATFORM + not in ["ec2", "gce", "oci", "openstack", "lxd_container", "lxd_vm"], + reason=f"Test hasn't been tested on {PLATFORM}.", +) +# TODO: The below comment likely isn't true anymore # No Azure because the cache gets purged every reboot, so we'll never # get to the point where we need to purge cache due to version change def test_cache_purged_on_version_change(client: IntegrationInstance): diff --git a/tests/integration_tests/modules/test_wireguard.py b/tests/integration_tests/modules/test_wireguard.py index e658a9df..e685a269 100644 --- a/tests/integration_tests/modules/test_wireguard.py +++ b/tests/integration_tests/modules/test_wireguard.py @@ -4,6 +4,8 @@ from pycloudlib.lxd.instance import LXDInstance from cloudinit.subp import subp from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import IS_UBUNTU ASCII_TEXT = "ASCII text" @@ -55,13 +57,13 @@ def load_wireguard_kernel_module_lxd(instance: LXDInstance): @pytest.mark.ci @pytest.mark.user_data(USER_DATA) -@pytest.mark.lxd_vm -@pytest.mark.gce -@pytest.mark.ec2 -@pytest.mark.azure -@pytest.mark.openstack -@pytest.mark.oci -@pytest.mark.ubuntu +@pytest.mark.skipif( + not IS_UBUNTU, reason="Hasn't been tested on other distros" +) +@pytest.mark.skipif( + PLATFORM not in ["lxd_vm", "gce", "ec2", "azure", "openstack", "oci"], + reason=f"Test hasn't been tested on {PLATFORM}", +) class TestWireguard: @pytest.mark.parametrize( "cmd,expected_out", @@ -119,8 +121,10 @@ class TestWireguard: @pytest.mark.ci @pytest.mark.user_data(USER_DATA) @pytest.mark.lxd_setup.with_args(load_wireguard_kernel_module_lxd) -@pytest.mark.lxd_container -@pytest.mark.ubuntu +@pytest.mark.skipif( + PLATFORM != "lxd_container", reason=f"Not testing on {PLATFORM}" +) +@pytest.mark.skipif(not IS_UBUNTU, reason="Has only been tested on Ubuntu") class TestWireguardWithoutKmod: def test_wireguard_tools_installed( self, class_client: IntegrationInstance diff --git a/tests/integration_tests/releases.py b/tests/integration_tests/releases.py new file mode 100644 index 00000000..b2ab9cee --- /dev/null +++ b/tests/integration_tests/releases.py @@ -0,0 +1,92 @@ +import functools +import logging +from typing import Optional + +from packaging import version + +from cloudinit import subp +from cloudinit.subp import ProcessExecutionError +from tests.integration_tests import integration_settings + +log = logging.getLogger("integration_testing") + + +def get_all_ubuntu_series() -> list: + """Use distro-info-data's ubuntu.csv to get a list of Ubuntu series""" + out = "" + try: + out, _err = subp.subp(["ubuntu-distro-info", "-a"]) + except ProcessExecutionError: + log.info( + "ubuntu-distro-info (from the distro-info package) must be" + " installed to guess Ubuntu os/release" + ) + return out.splitlines() + + +def ubuntu_version_from_series(series) -> str: + try: + out, _err = subp.subp( + ["ubuntu-distro-info", "--release", "--series", series] + ) + except subp.ProcessExecutionError as e: + raise ValueError( + f"'{series}' is not a recognized Ubuntu release" + ) from e + return out.strip().rstrip(" LTS") + + +@functools.total_ordering +class Release: + def __init__( + self, + os: str, + series: str, + version: str, + image_id: Optional[str] = None, + ): + self.os = os + self.series = series + self.version = version + self.image_id = image_id + + def __repr__(self): + return f"Release({self.os}, {self.version})" + + def __lt__(self, other: "Release"): + if self.os != other.os: + raise ValueError(f"{self.os} cannot be compared to {other.os}!") + return version.parse(self.version) < version.parse(other.version) + + @classmethod + def from_os_image( + cls, + os_image: str = integration_settings.OS_IMAGE, + ) -> "Release": + """Get the individual parts from an OS_IMAGE definition. + + Returns a namedtuple containing id, os, and release of the image.""" + parts = os_image.split("::", 2) + if len(parts) == 1: + image_id = None + os = "ubuntu" + series = parts[0] + version = ubuntu_version_from_series(series) + elif len(parts) == 4: + image_id, os, series, version = parts + else: + raise ValueError( + "OS_IMAGE must either contain release name or be in the form " + "of [::[::[::]]]" + ) + return cls(os, series, version, image_id) + + +BIONIC = Release("ubuntu", "bionic", "18.04") +FOCAL = Release("ubuntu", "focal", "20.04") +JAMMY = Release("ubuntu", "jammy", "22.04") +KINETIC = Release("ubuntu", "kinetic", "22.10") +LUNAR = Release("ubuntu", "lunar", "23.04") + +CURRENT_RELEASE = Release.from_os_image() +IS_UBUNTU = CURRENT_RELEASE.os == "ubuntu" diff --git a/tests/integration_tests/test_paths.py b/tests/integration_tests/test_paths.py index 14513c82..b63da5a4 100644 --- a/tests/integration_tests/test_paths.py +++ b/tests/integration_tests/test_paths.py @@ -10,6 +10,7 @@ from cloudinit.cmd.devel.logs import ( INSTALLER_APPORT_SENSITIVE_FILES, ) from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.releases import CURRENT_RELEASE, FOCAL from tests.integration_tests.util import verify_clean_log DEFAULT_CLOUD_DIR = "/var/lib/cloud" @@ -111,7 +112,10 @@ class TestHonorCloudDir: # because the test ensures nothing is running under /var/lib/cloud. # Since LXD is doing this and not cloud-init, we should just not run # on Bionic to avoid it. - @pytest.mark.not_bionic + @pytest.mark.skipif( + CURRENT_RELEASE < FOCAL, + reason="LXD inserts conflicting setup on releases prior to focal", + ) def test_honor_cloud_dir(self, custom_client: IntegrationInstance): """Integration test for LP: #1976564 diff --git a/tests/integration_tests/test_upgrade.py b/tests/integration_tests/test_upgrade.py index 5ef82e88..787bda44 100644 --- a/tests/integration_tests/test_upgrade.py +++ b/tests/integration_tests/test_upgrade.py @@ -4,8 +4,10 @@ import os import pytest -from tests.integration_tests.clouds import ImageSpecification, IntegrationCloud +from tests.integration_tests.clouds import IntegrationCloud from tests.integration_tests.conftest import get_validated_source +from tests.integration_tests.integration_settings import PLATFORM +from tests.integration_tests.releases import CURRENT_RELEASE, FOCAL, IS_UBUNTU from tests.integration_tests.util import verify_clean_log LOG = logging.getLogger("integration_testing.test_upgrade") @@ -41,23 +43,20 @@ hostname: SRU-worked """ +# The issues that we see on Bionic VMs don't appear anywhere +# else, including when calling KVM directly. It likely has to +# do with the extra lxd-agent setup happening on bionic. +# Given that we still have Bionic covered on all other platforms, +# the risk of skipping bionic here seems low enough. +@pytest.mark.skipif( + PLATFORM == "lxd_vm" and CURRENT_RELEASE < FOCAL, + reason="Update test doesn't run on Bionic LXD VMs", +) def test_clean_boot_of_upgraded_package(session_cloud: IntegrationCloud): source = get_validated_source(session_cloud) if not source.installs_new_version(): pytest.skip(UNSUPPORTED_INSTALL_METHOD_MSG.format(source)) return # type checking doesn't understand that skip raises - if ( - ImageSpecification.from_os_image().release == "bionic" - and session_cloud.settings.PLATFORM == "lxd_vm" - ): - # The issues that we see on Bionic VMs don't appear anywhere - # else, including when calling KVM directly. It likely has to - # do with the extra lxd-agent setup happening on bionic. - # Given that we still have Bionic covered on all other platforms, - # the risk of skipping bionic here seems low enough. - pytest.skip("Upgrade test doesn't run on LXD VMs and bionic") - return - launch_kwargs = { "image_id": session_cloud.initial_image_id, } @@ -172,11 +171,11 @@ def test_clean_boot_of_upgraded_package(session_cloud: IntegrationCloud): @pytest.mark.ci -@pytest.mark.ubuntu +@pytest.mark.skipif(not IS_UBUNTU, reason="Only ever tested on Ubuntu") def test_subsequent_boot_of_upgraded_package(session_cloud: IntegrationCloud): source = get_validated_source(session_cloud) if not source.installs_new_version(): - if os.environ.get("TRAVIS"): + if os.environ.get("GITHUB_ACTIONS"): # If this isn't running on CI, we should know pytest.fail(UNSUPPORTED_INSTALL_METHOD_MSG.format(source)) else: diff --git a/tox.ini b/tox.ini index b49272ac..c0a3b868 100644 --- a/tox.ini +++ b/tox.ini @@ -296,27 +296,16 @@ markers = adhoc: only run on adhoc basis, not in any CI environment (travis or jenkins) allow_all_subp: allow all subp usage (disable_subp_usage) allow_subp_for: allow subp usage for the given commands (disable_subp_usage) - azure: test will only run on Azure platform ci: run this integration test as part of CI test runs ds_sys_cfg: a sys_cfg dict to be used by datasource fixtures - ec2: test will only run on EC2 platform - gce: test will only run on GCE platform hypothesis_slow: hypothesis test too slow to run as unit test - ibm: test will only run on IBM platform instance_name: the name to be used for the test instance integration_cloud_args: args for IntegrationCloud customization is_iscsi: whether is an instance has iscsi net cfg or not lxd_config_dict: set the config_dict passed on LXD instance creation - lxd_container: test will only run in LXD container lxd_setup: specify callable to be called between init and start lxd_use_exec: `execute` will use `lxc exec` instead of SSH - lxd_vm: test will only run in LXD VM - no_container: test cannot run in a container - not_bionic: test cannot run on the bionic release - oci: test will only run on OCI platform - openstack: test will only run on openstack platform serial: tests that do not work in parallel, skipped with py3-fast - ubuntu: this test should run on Ubuntu unstable: skip this test because it is flakey user_data: the user data to be passed to the test instance allow_dns_lookup: disable autochecking for host network configuration -- cgit v1.2.1