# This file is part of cloud-init. See LICENSE file for license information. """A set of somewhat unrelated tests that can be combined into a single instance launch. Generally tests should only be added here if a failure of the test would be unlikely to affect the running of another test using the same instance launch. Most independent module coherence tests can go here. """ import json import pytest import re from tests.integration_tests.clouds import ImageSpecification from tests.integration_tests.instances import IntegrationInstance from tests.integration_tests.util import ( verify_clean_log, verify_ordered_items_in_text, ) USER_DATA = """\ #cloud-config apt: primary: - arches: [default] uri: http://us.archive.ubuntu.com/ubuntu/ byobu_by_default: enable final_message: | This is my final message! $version $timestamp $datasource $uptime locale: en_GB.UTF-8 locale_configfile: /etc/default/locale ntp: servers: ['ntp.ubuntu.com'] rsyslog: configs: - "*.* @@127.0.0.1" - filename: 0-basic-config.conf content: | module(load="imtcp") input(type="imtcp" port="514") $template RemoteLogs,"/var/tmp/rsyslog.log" *.* ?RemoteLogs & ~ remotes: me: "127.0.0.1" runcmd: - echo 'hello world' > /var/tmp/runcmd_output - logger "My test log" """ @pytest.mark.ci @pytest.mark.user_data(USER_DATA) 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. 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') # 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' '{}.*\n' 'DataSource.*\n' r'\d+\.\d+' ).format(today) assert re.search(expected, log) def test_ntp_with_apt(self, class_client: IntegrationInstance): """LP #1628337. cloud-init tries to install NTP before even configuring the archives. """ client = class_client log = client.read_from_file('/var/log/cloud-init.log') assert 'W: Failed to fetch' not in log assert 'W: Some index files failed to download' not in log assert 'E: Unable to locate package ntp' not in log def test_byobu(self, class_client: IntegrationInstance): """Test byobu configured as enabled by default.""" client = class_client assert client.execute('test -e "/etc/byobu/autolaunch"').ok def test_configured_locale(self, class_client: IntegrationInstance): """Test locale can be configured correctly.""" client = class_client default_locale = client.read_from_file('/etc/default/locale') assert 'LANG=en_GB.UTF-8' in default_locale locale_a = client.execute('locale -a') verify_ordered_items_in_text([ 'en_GB.utf8', 'en_US.utf8' ], locale_a) locale_gen = client.execute( "cat /etc/locale.gen | grep -v '^#' | uniq" ) verify_ordered_items_in_text([ 'en_GB.UTF-8', 'en_US.UTF-8' ], locale_gen) def test_rsyslog(self, class_client: IntegrationInstance): """Test rsyslog is configured correctly.""" client = class_client assert 'My test log' in client.read_from_file('/var/tmp/rsyslog.log') def test_runcmd(self, class_client: IntegrationInstance): """Test runcmd works as expected""" client = class_client 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""" client = class_client status_file = client.read_from_file('/run/cloud-init/status.json') status_json = json.loads(status_file)['v1'] for stage in ('init', 'init-local', 'modules-config', 'modules-final'): assert status_json[stage]['errors'] == [] result_file = client.read_from_file('/run/cloud-init/result.json') result_json = json.loads(result_file)['v1'] assert result_json['errors'] == [] log = client.read_from_file('/var/log/cloud-init.log') verify_clean_log(log) def _check_common_metadata(self, data): assert data['base64_encoded_keys'] == [] assert data['merged_cfg'] == 'redacted for non-root user' image_spec = ImageSpecification.from_os_image() assert data['sys_info']['dist'][0] == image_spec.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['machine'] == 'x86_64' assert re.match(r'3.\d\.\d', v1_data['python_version']) @pytest.mark.lxd_container def test_instance_json_lxd(self, class_client: IntegrationInstance): client = class_client instance_json_file = client.read_from_file( '/run/cloud-init/instance-data.json') data = json.loads(instance_json_file) self._check_common_metadata(data) 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 v1_data['availability_zone'] is None assert v1_data['instance_id'] == client.instance.name assert v1_data['local_hostname'] == client.instance.name assert v1_data['region'] is None @pytest.mark.lxd_vm def test_instance_json_lxd_vm(self, class_client: IntegrationInstance): client = class_client instance_json_file = client.read_from_file( '/run/cloud-init/instance-data.json') data = json.loads(instance_json_file) self._check_common_metadata(data) v1_data = data['v1'] assert v1_data['cloud_name'] == 'unknown' assert v1_data['platform'] == 'lxd' 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 assert v1_data['region'] is None @pytest.mark.ec2 def test_instance_json_ec2(self, class_client: IntegrationInstance): client = class_client instance_json_file = client.read_from_file( '/run/cloud-init/instance-data.json') data = json.loads(instance_json_file) v1_data = data['v1'] assert v1_data['cloud_name'] == 'aws' assert v1_data['platform'] == 'ec2' assert v1_data['subplatform'].startswith('metadata') assert v1_data[ 'availability_zone'] == client.instance.availability_zone assert v1_data['instance_id'] == client.instance.name assert v1_data['local_hostname'].startswith('ip-') assert v1_data['region'] == client.cloud.cloud_instance.region