diff options
Diffstat (limited to 'tests')
19 files changed, 212 insertions, 81 deletions
diff --git a/tests/integration_tests/bugs/test_gh632.py b/tests/integration_tests/bugs/test_gh632.py index 3c1f9347..f3702a2e 100644 --- a/tests/integration_tests/bugs/test_gh632.py +++ b/tests/integration_tests/bugs/test_gh632.py @@ -3,16 +3,15 @@ Verify that if cloud-init is using DataSourceRbxCloud, there is no traceback if the metadata disk cannot be found. """ - import pytest from tests.integration_tests.instances import IntegrationInstance +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.sru_2020_11 def test_datasource_rbx_no_stacktrace(client: IntegrationInstance): client.write_to_file( '/etc/cloud/cloud.cfg.d/90_dpkg.cfg', @@ -26,8 +25,7 @@ def test_datasource_rbx_no_stacktrace(client: IntegrationInstance): client.restart() log = client.read_from_file('/var/log/cloud-init.log') - assert 'WARNING' not in log - assert 'Traceback' not in log + verify_clean_log(log) assert 'Failed to load metadata and userdata' not in log assert ("Getting data from <class 'cloudinit.sources.DataSourceRbxCloud." "DataSourceRbxCloud'> failed") not in log diff --git a/tests/integration_tests/bugs/test_gh868.py b/tests/integration_tests/bugs/test_gh868.py index 838efca6..73c03451 100644 --- a/tests/integration_tests/bugs/test_gh868.py +++ b/tests/integration_tests/bugs/test_gh868.py @@ -1,6 +1,8 @@ """Ensure no Traceback when 'chef_license' is set""" import pytest + from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.util import verify_clean_log USERDATA = """\ @@ -17,4 +19,4 @@ chef: @pytest.mark.user_data(USERDATA) def test_chef_license(client: IntegrationInstance): log = client.read_from_file('/var/log/cloud-init.log') - assert 'Traceback' not in log + verify_clean_log(log) diff --git a/tests/integration_tests/bugs/test_lp1813396.py b/tests/integration_tests/bugs/test_lp1813396.py index 68b96b1d..27d41c2b 100644 --- a/tests/integration_tests/bugs/test_lp1813396.py +++ b/tests/integration_tests/bugs/test_lp1813396.py @@ -29,6 +29,5 @@ def test_gpg_no_tty(client: IntegrationInstance): "'--keyserver=keyserver.ubuntu.com', '--recv-keys', 'E4D304DF'] " "with allowed return codes [0] (shell=False, capture=True)", "Imported key 'E4D304DF' from keyserver 'keyserver.ubuntu.com'", - "finish: modules-config/config-apt-configure: SUCCESS", ] verify_ordered_items_in_text(to_verify, log) diff --git a/tests/integration_tests/bugs/test_lp1835584.py b/tests/integration_tests/bugs/test_lp1835584.py index 660d2a2a..732f2179 100644 --- a/tests/integration_tests/bugs/test_lp1835584.py +++ b/tests/integration_tests/bugs/test_lp1835584.py @@ -59,6 +59,9 @@ def _check_iid_insensitive_across_kernel_upgrade( result = instance.execute("apt-get install linux-azure --assume-yes") if not result.ok: pytest.fail("Unable to install linux-azure kernel: {}".format(result)) + # Remove ubuntu-azure-fips metapkg which mandates FIPS-flavour kernel + result = instance.execute("ua disable fips --assume-yes") + assert result.ok, "Unable to disable fips: {}".format(result) instance.restart() new_kernel = instance.execute("uname -r").strip() assert orig_kernel != new_kernel diff --git a/tests/integration_tests/bugs/test_lp1886531.py b/tests/integration_tests/bugs/test_lp1886531.py index 058ea8bb..6dd61222 100644 --- a/tests/integration_tests/bugs/test_lp1886531.py +++ b/tests/integration_tests/bugs/test_lp1886531.py @@ -11,6 +11,8 @@ https://bugs.launchpad.net/ubuntu/+source/cloud-init/+bug/1886531 """ import pytest +from tests.integration_tests.util import verify_clean_log + USER_DATA = """\ #cloud-config @@ -24,4 +26,4 @@ class TestLp1886531: @pytest.mark.user_data(USER_DATA) def test_lp1886531(self, client): log_content = client.read_from_file("/var/log/cloud-init.log") - assert "WARNING" not in log_content + verify_clean_log(log_content) diff --git a/tests/integration_tests/bugs/test_lp1898997.py b/tests/integration_tests/bugs/test_lp1898997.py index bde93d06..909bc690 100644 --- a/tests/integration_tests/bugs/test_lp1898997.py +++ b/tests/integration_tests/bugs/test_lp1898997.py @@ -10,7 +10,9 @@ network configuration, and confirms that the bridge can be used to ping the default gateway. """ import pytest + from tests.integration_tests import random_mac_address +from tests.integration_tests.util import verify_clean_log MAC_ADDRESS = random_mac_address() @@ -59,7 +61,7 @@ class TestInterfaceListingWithOpenvSwitch: cloudinit_output = client.read_from_file("/var/log/cloud-init.log") # Confirm that the network configuration was applied successfully - assert "WARN" not in cloudinit_output + verify_clean_log(cloudinit_output) # Confirm that the applied network config created the OVS bridge assert "ovs-br" in client.execute("ip addr") diff --git a/tests/integration_tests/instances.py b/tests/integration_tests/instances.py index 63e0e630..8f66bf43 100644 --- a/tests/integration_tests/instances.py +++ b/tests/integration_tests/instances.py @@ -9,6 +9,7 @@ from pycloudlib.instance import BaseInstance from pycloudlib.result import Result from tests.integration_tests import integration_settings +from tests.integration_tests.util import retry try: from typing import TYPE_CHECKING @@ -142,26 +143,31 @@ class IntegrationInstance: snapshot_id = self.snapshot() self.cloud.snapshot_id = snapshot_id + # assert with retry because we can compete with apt already running in the + # background and get: E: Could not get lock /var/lib/apt/lists/lock - open + # (11: Resource temporarily unavailable) + + @retry(tries=30, delay=1) def install_proposed_image(self): log.info('Installing proposed image') - remote_script = ( + assert self.execute( 'echo deb "http://archive.ubuntu.com/ubuntu ' - '$(lsb_release -sc)-proposed main" | ' - 'tee /etc/apt/sources.list.d/proposed.list\n' - 'apt-get update -q\n' - 'apt-get install -qy cloud-init' - ) - self.execute(remote_script) + '$(lsb_release -sc)-proposed main" >> ' + '/etc/apt/sources.list.d/proposed.list' + ).ok + assert self.execute('apt-get update -q').ok + assert self.execute('apt-get install -qy cloud-init').ok + @retry(tries=30, delay=1) def install_ppa(self): log.info('Installing PPA') - remote_script = ( - 'add-apt-repository {repo} -y && ' - 'apt-get update -q && ' - 'apt-get install -qy cloud-init' - ).format(repo=self.settings.CLOUD_INIT_SOURCE) - self.execute(remote_script) + assert self.execute('add-apt-repository {} -y'.format( + self.settings.CLOUD_INIT_SOURCE) + ).ok + assert self.execute('apt-get update -q').ok + assert self.execute('apt-get install -qy cloud-init').ok + @retry(tries=30, delay=1) def install_deb(self): log.info('Installing deb package') deb_path = integration_settings.CLOUD_INIT_SOURCE @@ -170,13 +176,13 @@ class IntegrationInstance: self.push_file( local_path=integration_settings.CLOUD_INIT_SOURCE, remote_path=remote_path) - remote_script = 'dpkg -i {path}'.format(path=remote_path) - self.execute(remote_script) + assert self.execute('dpkg -i {path}'.format(path=remote_path)).ok + @retry(tries=30, delay=1) def upgrade_cloud_init(self): log.info('Upgrading cloud-init to latest version in archive') - self.execute("apt-get update -q") - self.execute("apt-get install -qy cloud-init") + assert self.execute("apt-get update -q").ok + assert self.execute("apt-get install -qy cloud-init").ok def __enter__(self): return self diff --git a/tests/integration_tests/modules/test_combined.py b/tests/integration_tests/modules/test_combined.py index 27f3c074..9cd1648a 100644 --- a/tests/integration_tests/modules/test_combined.py +++ b/tests/integration_tests/modules/test_combined.py @@ -8,14 +8,15 @@ here. import json import pytest import re -from datetime import date from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.instances import IntegrationInstance -from tests.integration_tests.util import verify_ordered_items_in_text +from tests.integration_tests.util import ( + verify_clean_log, + verify_ordered_items_in_text, +) USER_DATA = """\ -## template: jinja #cloud-config apt: primary: @@ -33,8 +34,7 @@ locale_configfile: /etc/default/locale ntp: servers: ['ntp.ubuntu.com'] runcmd: - - echo {{ds.meta_data.local_hostname}} > /var/tmp/runcmd_output - - echo {{merged_cfg.def_log_file}} >> /var/tmp/runcmd_output + - echo 'hello world' > /var/tmp/runcmd_output """ @@ -44,11 +44,17 @@ class TestCombined: def test_final_message(self, class_client: IntegrationInstance): """Test that final_message module works as expected. - Also tests LP 1511485: final_message is silent + Also tests LP 1511485: final_message is silent. + + It's possible that if this test is run within a minute or so of + midnight that we'll see a failure because the day in the logs + is different from the day specified in the test definition. """ client = class_client log = client.read_from_file('/var/log/cloud-init.log') - today = date.today().strftime('%a, %d %b %Y') + # Get date on host rather than locally as our host could be in a + # wildly different timezone (or more likely recording UTC) + today = client.execute('date "+%a, %d %b %Y"') expected = ( 'This is my final message!\n' r'\d+\.\d+.*\n' @@ -96,21 +102,10 @@ class TestCombined: 'en_US.UTF-8' ], locale_gen) - def test_runcmd_with_variable_substitution( - self, class_client: IntegrationInstance - ): - """Test runcmd, while including jinja substitution. - - Ensure we can also substitue variables from instance-data-sensitive - LP: #1931392 - """ + def test_runcmd(self, class_client: IntegrationInstance): + """Test runcmd works as expected""" client = class_client - expected = [ - client.execute('hostname').stdout.strip(), - '/var/log/cloud-init.log', - ] - output = client.read_from_file('/var/tmp/runcmd_output') - verify_ordered_items_in_text(expected, output) + assert 'hello world' == client.read_from_file('/var/tmp/runcmd_output') def test_no_problems(self, class_client: IntegrationInstance): """Test no errors, warnings, or tracebacks""" @@ -124,8 +119,7 @@ class TestCombined: assert result_json['errors'] == [] log = client.read_from_file('/var/log/cloud-init.log') - assert 'WARN' not in log - assert 'Traceback' not in log + verify_clean_log(log) def _check_common_metadata(self, data): assert data['base64_encoded_keys'] == [] @@ -171,8 +165,10 @@ class TestCombined: v1_data = data['v1'] assert v1_data['cloud_name'] == 'unknown' assert v1_data['platform'] == 'lxd' - assert v1_data['subplatform'] == ( - 'seed-dir (/var/lib/cloud/seed/nocloud-net)') + assert any([ + '/var/lib/cloud/seed/nocloud-net' in v1_data['subplatform'], + '/dev/sr0' in v1_data['subplatform'] + ]) assert v1_data['availability_zone'] is None assert v1_data['instance_id'] == client.instance.name assert v1_data['local_hostname'] == client.instance.name diff --git a/tests/integration_tests/modules/test_disk_setup.py b/tests/integration_tests/modules/test_disk_setup.py index 1fc96c52..9c9edc46 100644 --- a/tests/integration_tests/modules/test_disk_setup.py +++ b/tests/integration_tests/modules/test_disk_setup.py @@ -6,6 +6,7 @@ from pycloudlib.lxd.instance import LXDInstance from cloudinit.subp import subp from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.util import verify_clean_log DISK_PATH = '/tmp/test_disk_setup_{}'.format(uuid4()) @@ -59,8 +60,7 @@ class TestDeviceAliases: ) in log assert 'changed my_alias.1 => /dev/sdb1' in log assert 'changed my_alias.2 => /dev/sdb2' in log - assert 'WARN' not in log - assert 'Traceback' not in log + verify_clean_log(log) lsblk = json.loads(client.execute('lsblk --json')) sdb = [x for x in lsblk['blockdevices'] if x['name'] == 'sdb'][0] @@ -120,8 +120,7 @@ class TestPartProbeAvailability: """ def _verify_first_disk_setup(self, client, log): - assert 'Traceback' not in log - assert 'WARN' not in log + verify_clean_log(log) lsblk = json.loads(client.execute('lsblk --json')) sdb = [x for x in lsblk['blockdevices'] if x['name'] == 'sdb'][0] assert len(sdb['children']) == 2 @@ -167,8 +166,7 @@ class TestPartProbeAvailability: client.restart() # Assert new setup works as expected - assert 'Traceback' not in log - assert 'WARN' not in log + verify_clean_log(log) lsblk = json.loads(client.execute('lsblk --json')) sdb = [x for x in lsblk['blockdevices'] if x['name'] == 'sdb'][0] diff --git a/tests/integration_tests/modules/test_hotplug.py b/tests/integration_tests/modules/test_hotplug.py index a42d1c8c..88cd8c16 100644 --- a/tests/integration_tests/modules/test_hotplug.py +++ b/tests/integration_tests/modules/test_hotplug.py @@ -40,6 +40,10 @@ def _get_ip_addr(client): @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.user_data(USER_DATA) def test_hotplug_add_remove(client: IntegrationInstance): ips_before = _get_ip_addr(client) diff --git a/tests/integration_tests/modules/test_jinja_templating.py b/tests/integration_tests/modules/test_jinja_templating.py new file mode 100644 index 00000000..35b8ee2d --- /dev/null +++ b/tests/integration_tests/modules/test_jinja_templating.py @@ -0,0 +1,30 @@ +# This file is part of cloud-init. See LICENSE file for license information. +import pytest + +from tests.integration_tests.instances import IntegrationInstance +from tests.integration_tests.util import verify_ordered_items_in_text + + +USER_DATA = """\ +## template: jinja +#cloud-config +runcmd: + - echo {{v1.local_hostname}} > /var/tmp/runcmd_output + - echo {{merged_cfg._doc}} >> /var/tmp/runcmd_output +""" + + +@pytest.mark.user_data(USER_DATA) +def test_runcmd_with_variable_substitution(client: IntegrationInstance): + """Test jinja substitution. + + Ensure we can also substitute variables from instance-data-sensitive + LP: #1931392 + """ + expected = [ + client.execute('hostname').stdout.strip(), + ('Merged cloud-init system config from /etc/cloud/cloud.cfg and ' + '/etc/cloud/cloud.cfg.d/') + ] + output = client.read_from_file('/var/tmp/runcmd_output') + verify_ordered_items_in_text(expected, output) diff --git a/tests/integration_tests/modules/test_lxd_bridge.py b/tests/integration_tests/modules/test_lxd_bridge.py index cbf11179..65dce3c7 100644 --- a/tests/integration_tests/modules/test_lxd_bridge.py +++ b/tests/integration_tests/modules/test_lxd_bridge.py @@ -6,6 +6,8 @@ import pytest import yaml +from tests.integration_tests.util import verify_clean_log + USER_DATA = """\ #cloud-config @@ -38,7 +40,7 @@ class TestLxdBridge: def test_bridge(self, class_client): """Check that the given bridge is configured""" cloud_init_log = class_client.read_from_file("/var/log/cloud-init.log") - assert "WARN" not in cloud_init_log + verify_clean_log(cloud_init_log) # The bridge should exist assert class_client.execute("ip addr show lxdbr0") diff --git a/tests/integration_tests/modules/test_ntp_servers.py b/tests/integration_tests/modules/test_ntp_servers.py index 7a799139..59241faa 100644 --- a/tests/integration_tests/modules/test_ntp_servers.py +++ b/tests/integration_tests/modules/test_ntp_servers.py @@ -78,6 +78,8 @@ CHRONY_DATA = """\ ntp: enabled: true ntp_client: chrony + servers: + - 172.16.15.14 """ @@ -89,7 +91,7 @@ def test_chrony(client: IntegrationInstance): else: chrony_conf = '/etc/chrony/chrony.conf' contents = client.read_from_file(chrony_conf) - assert '.pool.ntp.org' in contents + assert 'server 172.16.15.14' in contents TIMESYNCD_DATA = """\ @@ -97,6 +99,8 @@ TIMESYNCD_DATA = """\ ntp: enabled: true ntp_client: systemd-timesyncd + servers: + - 172.16.15.14 """ @@ -106,7 +110,7 @@ def test_timesyncd(client: IntegrationInstance): contents = client.read_from_file( '/etc/systemd/timesyncd.conf.d/cloud-init.conf' ) - assert '.pool.ntp.org' in contents + assert 'NTP=172.16.15.14' in contents EMPTY_NTP = """\ diff --git a/tests/integration_tests/modules/test_set_password.py b/tests/integration_tests/modules/test_set_password.py index d7cf91a5..ac9db19d 100644 --- a/tests/integration_tests/modules/test_set_password.py +++ b/tests/integration_tests/modules/test_set_password.py @@ -13,6 +13,8 @@ import crypt import pytest import yaml +from tests.integration_tests.util import retry + COMMON_USER_DATA = """\ #cloud-config @@ -129,6 +131,7 @@ class Mixin: assert "dick:" not in cloud_init_output assert "harry:" not in cloud_init_output + @retry(tries=30, delay=1) def test_random_passwords_emitted_to_serial_console(self, class_client): """We should emit passwords to the serial console. (LP: #1918303)""" try: @@ -137,6 +140,15 @@ class Mixin: # Assume that an exception here means that we can't use the console # log pytest.skip("NotImplementedError when requesting console log") + return + if console_log.lower() == 'no console output': + # This test retries because we might not have the full console log + # on the first fetch. However, if we have no console output + # at all, we don't want to keep retrying as that would trigger + # another 5 minute wait on the pycloudlib side, which could + # leave us waiting for a couple hours + pytest.fail('no console output') + return assert "dick:" in console_log assert "harry:" in console_log diff --git a/tests/integration_tests/modules/test_ssh_keysfile.py b/tests/integration_tests/modules/test_ssh_keysfile.py index 5c720578..b39454e6 100644 --- a/tests/integration_tests/modules/test_ssh_keysfile.py +++ b/tests/integration_tests/modules/test_ssh_keysfile.py @@ -38,9 +38,15 @@ def common_verify(client, expected_keys): # Ensure key is in the key file contents = client.read_from_file(filename) if user in ['ubuntu', 'root']: - # Our personal public key gets added by pycloudlib lines = contents.split('\n') - assert len(lines) == 2 + if user == 'root': + # Our personal public key gets added by pycloudlib in + # addition to the default `ssh_authorized_keys` + assert len(lines) == 2 + else: + # Clouds will insert the keys we've added to our accounts + # or for our launches + assert len(lines) >= 2 assert keys.public_key.strip() in contents else: assert contents.strip() == keys.public_key.strip() diff --git a/tests/integration_tests/modules/test_user_events.py b/tests/integration_tests/modules/test_user_events.py index a45cad72..ee8f05ae 100644 --- a/tests/integration_tests/modules/test_user_events.py +++ b/tests/integration_tests/modules/test_user_events.py @@ -31,9 +31,12 @@ def _add_dummy_bridge_to_netplan(client: IntegrationInstance): @pytest.mark.gce @pytest.mark.oci @pytest.mark.openstack +@pytest.mark.azure @pytest.mark.not_xenial 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: + pytest.skip("network config disabled. Test doesn't apply") assert 'Applying network configuration' in log assert 'dummy0' not in client.execute('ls /sys/class/net') @@ -43,6 +46,12 @@ def test_boot_event_disabled_by_default(client: IntegrationInstance): client.restart() log2 = client.read_from_file('/var/log/cloud-init.log') + if 'cache invalid in datasource' in log2: + # Invalid cache will get cleared, meaning we'll create a new + # "instance" and apply networking config, so events aren't + # really relevant here + pytest.skip("Test only valid for existing instances") + # We attempt to apply network config twice on every boot. # Ensure neither time works. assert 2 == len( @@ -62,25 +71,27 @@ def test_boot_event_disabled_by_default(client: IntegrationInstance): def _test_network_config_applied_on_reboot(client: IntegrationInstance): log = client.read_from_file('/var/log/cloud-init.log') + if 'network config is disabled' in log: + pytest.skip("network config disabled. Test doesn't apply") assert 'Applying network configuration' in log assert 'dummy0' not in client.execute('ls /sys/class/net') _add_dummy_bridge_to_netplan(client) client.execute('rm /var/log/cloud-init.log') client.restart() + log = client.read_from_file('/var/log/cloud-init.log') + if 'cache invalid in datasource' in log: + # Invalid cache will get cleared, meaning we'll create a new + # "instance" and apply networking config, so events aren't + # really relevant here + pytest.skip("Test only valid for existing instances") assert 'Event Allowed: scope=network EventType=boot' in log assert 'Applying network configuration' in log assert 'dummy0' not in client.execute('ls /sys/class/net') -@pytest.mark.azure -@pytest.mark.not_xenial -def test_boot_event_enabled_by_default(client: IntegrationInstance): - _test_network_config_applied_on_reboot(client) - - USER_DATA = """\ #cloud-config updates: diff --git a/tests/integration_tests/modules/test_version_change.py b/tests/integration_tests/modules/test_version_change.py index 4e9ab63f..ffea794a 100644 --- a/tests/integration_tests/modules/test_version_change.py +++ b/tests/integration_tests/modules/test_version_change.py @@ -1,7 +1,7 @@ from pathlib import Path from tests.integration_tests.instances import IntegrationInstance -from tests.integration_tests.util import ASSETS_DIR +from tests.integration_tests.util import ASSETS_DIR, verify_clean_log PICKLE_PATH = Path('/var/lib/cloud/instance/obj.pkl') @@ -10,8 +10,7 @@ TEST_PICKLE = ASSETS_DIR / 'test_version_change.pkl' def _assert_no_pickle_problems(log): assert 'Failed loading pickled blob' not in log - assert 'Traceback' not in log - assert 'WARN' not in log + verify_clean_log(log) def test_reboot_without_version_change(client: IntegrationInstance): @@ -30,7 +29,13 @@ def test_reboot_without_version_change(client: IntegrationInstance): client.push_file(TEST_PICKLE, PICKLE_PATH) client.restart() log = client.read_from_file('/var/log/cloud-init.log') - assert 'Failed loading pickled blob from {}'.format(PICKLE_PATH) in log + + # no cache found is an "expected" upgrade error, and + # "Failed" means we're unable to load the pickle + assert any([ + 'Failed loading pickled blob from {}'.format(PICKLE_PATH) in log, + 'no cache found' in log + ]) def test_cache_purged_on_version_change(client: IntegrationInstance): @@ -48,9 +53,13 @@ def test_log_message_on_missing_version_file(client: IntegrationInstance): # Start by pushing a pickle so we can see the log message client.push_file(TEST_PICKLE, PICKLE_PATH) client.execute("rm /var/lib/cloud/data/python-version") + client.execute("rm /var/log/cloud-init.log") client.restart() log = client.read_from_file('/var/log/cloud-init.log') - assert ( - 'Writing python-version file. ' - 'Cache compatibility status is currently unknown.' - ) in log + if 'no cache found' not in log: + # We don't expect the python version file to exist if we have no + # pre-existing cache + assert ( + 'Writing python-version file. ' + 'Cache compatibility status is currently unknown.' + ) in log diff --git a/tests/integration_tests/test_upgrade.py b/tests/integration_tests/test_upgrade.py index 376fcc96..e90a5f9d 100644 --- a/tests/integration_tests/test_upgrade.py +++ b/tests/integration_tests/test_upgrade.py @@ -5,6 +5,7 @@ import pytest from tests.integration_tests.clouds import ImageSpecification, IntegrationCloud from tests.integration_tests.conftest import get_validated_source +from tests.integration_tests.util import verify_clean_log LOG = logging.getLogger('integration_testing.test_upgrade') @@ -73,11 +74,15 @@ def test_clean_boot_of_upgraded_package(session_cloud: IntegrationCloud): pre_cloud_blame = instance.execute('cloud-init analyze blame') # Ensure no issues pre-upgrade + log = instance.read_from_file('/var/log/cloud-init.log') assert not json.loads(pre_result)['v1']['errors'] - log = instance.read_from_file('/var/log/cloud-init.log') - assert 'Traceback' not in log - assert 'WARN' not in log + try: + verify_clean_log(log) + except AssertionError: + LOG.warning( + 'There were errors/warnings/tracebacks pre-upgrade. ' + 'Any failures may be due to pre-upgrade problem') # Upgrade and reboot instance.install_new_cloud_init(source, take_snapshot=False) @@ -105,13 +110,21 @@ def test_clean_boot_of_upgraded_package(session_cloud: IntegrationCloud): assert not json.loads(pre_result)['v1']['errors'] log = instance.read_from_file('/var/log/cloud-init.log') - assert 'Traceback' not in log - assert 'WARN' not in log + verify_clean_log(log) # Ensure important things stayed the same assert pre_hostname == post_hostname assert pre_cloud_id == post_cloud_id - assert pre_result == post_result + try: + assert pre_result == post_result + except AssertionError: + if instance.settings.PLATFORM == 'azure': + pre_json = json.loads(pre_result) + post_json = json.loads(post_result) + assert pre_json['v1']['datasource'].startswith( + 'DataSourceAzure') + assert post_json['v1']['datasource'].startswith( + 'DataSourceAzure') assert pre_network == post_network # Calculate and log all the boot numbers diff --git a/tests/integration_tests/util.py b/tests/integration_tests/util.py index 80430eab..407096cd 100644 --- a/tests/integration_tests/util.py +++ b/tests/integration_tests/util.py @@ -28,6 +28,40 @@ def verify_ordered_items_in_text(to_verify: list, text: str): assert index > -1, "Expected item not found: '{}'".format(item) +def verify_clean_log(log): + """Assert no unexpected tracebacks or warnings in logs""" + warning_count = log.count('WARN') + expected_warnings = 0 + traceback_count = log.count('Traceback') + expected_tracebacks = 0 + + warning_texts = [ + # Consistently on all Azure launches: + # azure.py[WARNING]: No lease found; using default endpoint + 'No lease found; using default endpoint' + ] + traceback_texts = [] + if 'oracle' in log: + # LP: #1842752 + lease_exists_text = 'Stderr: RTNETLINK answers: File exists' + warning_texts.append(lease_exists_text) + traceback_texts.append(lease_exists_text) + # LP: #1833446 + fetch_error_text = ( + 'UrlError: 404 Client Error: Not Found for url: ' + 'http://169.254.169.254/latest/meta-data/') + warning_texts.append(fetch_error_text) + traceback_texts.append(fetch_error_text) + + for warning_text in warning_texts: + expected_warnings += log.count(warning_text) + for traceback_text in traceback_texts: + expected_tracebacks += log.count(traceback_text) + + assert warning_count == expected_warnings + assert traceback_count == expected_tracebacks + + @contextmanager def emit_dots_on_travis(): """emit a dot every 60 seconds if running on Travis. |