summaryrefslogtreecommitdiff
path: root/cloudinit/tests
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/tests')
-rw-r--r--cloudinit/tests/test_gpg.py54
-rw-r--r--cloudinit/tests/test_stages.py231
-rw-r--r--cloudinit/tests/test_util.py69
3 files changed, 353 insertions, 1 deletions
diff --git a/cloudinit/tests/test_gpg.py b/cloudinit/tests/test_gpg.py
new file mode 100644
index 00000000..0562b966
--- /dev/null
+++ b/cloudinit/tests/test_gpg.py
@@ -0,0 +1,54 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+"""Test gpg module."""
+
+from cloudinit import gpg
+from cloudinit import util
+from cloudinit.tests.helpers import CiTestCase
+
+import mock
+
+
+@mock.patch("cloudinit.gpg.time.sleep")
+@mock.patch("cloudinit.gpg.util.subp")
+class TestReceiveKeys(CiTestCase):
+ """Test the recv_key method."""
+
+ def test_retries_on_subp_exc(self, m_subp, m_sleep):
+ """retry should be done on gpg receive keys failure."""
+ retries = (1, 2, 4)
+ my_exc = util.ProcessExecutionError(
+ stdout='', stderr='', exit_code=2, cmd=['mycmd'])
+ m_subp.side_effect = (my_exc, my_exc, ('', ''))
+ gpg.recv_key("ABCD", "keyserver.example.com", retries=retries)
+ self.assertEqual([mock.call(1), mock.call(2)], m_sleep.call_args_list)
+
+ def test_raises_error_after_retries(self, m_subp, m_sleep):
+ """If the final run fails, error should be raised."""
+ naplen = 1
+ keyid, keyserver = ("ABCD", "keyserver.example.com")
+ m_subp.side_effect = util.ProcessExecutionError(
+ stdout='', stderr='', exit_code=2, cmd=['mycmd'])
+ with self.assertRaises(ValueError) as rcm:
+ gpg.recv_key(keyid, keyserver, retries=(naplen,))
+ self.assertIn(keyid, str(rcm.exception))
+ self.assertIn(keyserver, str(rcm.exception))
+ m_sleep.assert_called_with(naplen)
+
+ def test_no_retries_on_none(self, m_subp, m_sleep):
+ """retry should not be done if retries is None."""
+ m_subp.side_effect = util.ProcessExecutionError(
+ stdout='', stderr='', exit_code=2, cmd=['mycmd'])
+ with self.assertRaises(ValueError):
+ gpg.recv_key("ABCD", "keyserver.example.com", retries=None)
+ m_sleep.assert_not_called()
+
+ def test_expected_gpg_command(self, m_subp, m_sleep):
+ """Verify gpg is called with expected args."""
+ key, keyserver = ("DEADBEEF", "keyserver.example.com")
+ retries = (1, 2, 4)
+ m_subp.return_value = ('', '')
+ gpg.recv_key(key, keyserver, retries=retries)
+ m_subp.assert_called_once_with(
+ ['gpg', '--keyserver=%s' % keyserver, '--recv-keys', key],
+ capture=True)
+ m_sleep.assert_not_called()
diff --git a/cloudinit/tests/test_stages.py b/cloudinit/tests/test_stages.py
new file mode 100644
index 00000000..94b6b255
--- /dev/null
+++ b/cloudinit/tests/test_stages.py
@@ -0,0 +1,231 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+
+"""Tests related to cloudinit.stages module."""
+
+import os
+
+from cloudinit import stages
+from cloudinit import sources
+
+from cloudinit.event import EventType
+from cloudinit.util import write_file
+
+from cloudinit.tests.helpers import CiTestCase, mock
+
+TEST_INSTANCE_ID = 'i-testing'
+
+
+class FakeDataSource(sources.DataSource):
+
+ def __init__(self, paths=None, userdata=None, vendordata=None,
+ network_config=''):
+ super(FakeDataSource, self).__init__({}, None, paths=paths)
+ self.metadata = {'instance-id': TEST_INSTANCE_ID}
+ self.userdata_raw = userdata
+ self.vendordata_raw = vendordata
+ self._network_config = None
+ if network_config: # Permit for None value to setup attribute
+ self._network_config = network_config
+
+ @property
+ def network_config(self):
+ return self._network_config
+
+ def _get_data(self):
+ return True
+
+
+class TestInit(CiTestCase):
+ with_logs = True
+
+ def setUp(self):
+ super(TestInit, self).setUp()
+ self.tmpdir = self.tmp_dir()
+ self.init = stages.Init()
+ # Setup fake Paths for Init to reference
+ self.init._cfg = {'system_info': {
+ 'distro': 'ubuntu', 'paths': {'cloud_dir': self.tmpdir,
+ 'run_dir': self.tmpdir}}}
+ self.init.datasource = FakeDataSource(paths=self.init.paths)
+
+ def test_wb__find_networking_config_disabled(self):
+ """find_networking_config returns no config when disabled."""
+ disable_file = os.path.join(
+ self.init.paths.get_cpath('data'), 'upgraded-network')
+ write_file(disable_file, '')
+ self.assertEqual(
+ (None, disable_file),
+ self.init._find_networking_config())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_disabled_by_kernel(self, m_cmdline):
+ """find_networking_config returns when disabled by kernel cmdline."""
+ m_cmdline.return_value = {'config': 'disabled'}
+ self.assertEqual(
+ (None, 'cmdline'),
+ self.init._find_networking_config())
+ self.assertEqual('DEBUG: network config disabled by cmdline\n',
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_disabled_by_datasrc(self, m_cmdline):
+ """find_networking_config returns when disabled by datasource cfg."""
+ m_cmdline.return_value = {} # Kernel doesn't disable networking
+ self.init._cfg = {'system_info': {'paths': {'cloud_dir': self.tmpdir}},
+ 'network': {}} # system config doesn't disable
+
+ self.init.datasource = FakeDataSource(
+ network_config={'config': 'disabled'})
+ self.assertEqual(
+ (None, 'ds'),
+ self.init._find_networking_config())
+ self.assertEqual('DEBUG: network config disabled by ds\n',
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_disabled_by_sysconfig(self, m_cmdline):
+ """find_networking_config returns when disabled by system config."""
+ m_cmdline.return_value = {} # Kernel doesn't disable networking
+ self.init._cfg = {'system_info': {'paths': {'cloud_dir': self.tmpdir}},
+ 'network': {'config': 'disabled'}}
+ self.assertEqual(
+ (None, 'system_cfg'),
+ self.init._find_networking_config())
+ self.assertEqual('DEBUG: network config disabled by system_cfg\n',
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_returns_kernel(self, m_cmdline):
+ """find_networking_config returns kernel cmdline config if present."""
+ expected_cfg = {'config': ['fakekernel']}
+ m_cmdline.return_value = expected_cfg
+ self.init._cfg = {'system_info': {'paths': {'cloud_dir': self.tmpdir}},
+ 'network': {'config': ['fakesys_config']}}
+ self.init.datasource = FakeDataSource(
+ network_config={'config': ['fakedatasource']})
+ self.assertEqual(
+ (expected_cfg, 'cmdline'),
+ self.init._find_networking_config())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_returns_system_cfg(self, m_cmdline):
+ """find_networking_config returns system config when present."""
+ m_cmdline.return_value = {} # No kernel network config
+ expected_cfg = {'config': ['fakesys_config']}
+ self.init._cfg = {'system_info': {'paths': {'cloud_dir': self.tmpdir}},
+ 'network': expected_cfg}
+ self.init.datasource = FakeDataSource(
+ network_config={'config': ['fakedatasource']})
+ self.assertEqual(
+ (expected_cfg, 'system_cfg'),
+ self.init._find_networking_config())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_returns_datasrc_cfg(self, m_cmdline):
+ """find_networking_config returns datasource net config if present."""
+ m_cmdline.return_value = {} # No kernel network config
+ # No system config for network in setUp
+ expected_cfg = {'config': ['fakedatasource']}
+ self.init.datasource = FakeDataSource(network_config=expected_cfg)
+ self.assertEqual(
+ (expected_cfg, 'ds'),
+ self.init._find_networking_config())
+
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_returns_fallback(self, m_cmdline):
+ """find_networking_config returns fallback config if not defined."""
+ m_cmdline.return_value = {} # Kernel doesn't disable networking
+ # Neither datasource nor system_info disable or provide network
+
+ fake_cfg = {'config': [{'type': 'physical', 'name': 'eth9'}],
+ 'version': 1}
+
+ def fake_generate_fallback():
+ return fake_cfg
+
+ # Monkey patch distro which gets cached on self.init
+ distro = self.init.distro
+ distro.generate_fallback_config = fake_generate_fallback
+ self.assertEqual(
+ (fake_cfg, 'fallback'),
+ self.init._find_networking_config())
+ self.assertNotIn('network config disabled', self.logs.getvalue())
+
+ def test_apply_network_config_disabled(self):
+ """Log when network is disabled by upgraded-network."""
+ disable_file = os.path.join(
+ self.init.paths.get_cpath('data'), 'upgraded-network')
+
+ def fake_network_config():
+ return (None, disable_file)
+
+ self.init._find_networking_config = fake_network_config
+
+ self.init.apply_network_config(True)
+ self.assertIn(
+ 'INFO: network config is disabled by %s' % disable_file,
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.distros.ubuntu.Distro')
+ def test_apply_network_on_new_instance(self, m_ubuntu):
+ """Call distro apply_network_config methods on is_new_instance."""
+ net_cfg = {
+ 'version': 1, 'config': [
+ {'subnets': [{'type': 'dhcp'}], 'type': 'physical',
+ 'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
+
+ def fake_network_config():
+ return net_cfg, 'fallback'
+
+ self.init._find_networking_config = fake_network_config
+ self.init.apply_network_config(True)
+ self.init.distro.apply_network_config_names.assert_called_with(net_cfg)
+ self.init.distro.apply_network_config.assert_called_with(
+ net_cfg, bring_up=True)
+
+ @mock.patch('cloudinit.distros.ubuntu.Distro')
+ def test_apply_network_on_same_instance_id(self, m_ubuntu):
+ """Only call distro.apply_network_config_names on same instance id."""
+ old_instance_id = os.path.join(
+ self.init.paths.get_cpath('data'), 'instance-id')
+ write_file(old_instance_id, TEST_INSTANCE_ID)
+ net_cfg = {
+ 'version': 1, 'config': [
+ {'subnets': [{'type': 'dhcp'}], 'type': 'physical',
+ 'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
+
+ def fake_network_config():
+ return net_cfg, 'fallback'
+
+ self.init._find_networking_config = fake_network_config
+ self.init.apply_network_config(True)
+ self.init.distro.apply_network_config_names.assert_called_with(net_cfg)
+ self.init.distro.apply_network_config.assert_not_called()
+ self.assertIn(
+ 'No network config applied. Neither a new instance'
+ " nor datasource network update on '%s' event" % EventType.BOOT,
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.distros.ubuntu.Distro')
+ def test_apply_network_on_datasource_allowed_event(self, m_ubuntu):
+ """Apply network if datasource.update_metadata permits BOOT event."""
+ old_instance_id = os.path.join(
+ self.init.paths.get_cpath('data'), 'instance-id')
+ write_file(old_instance_id, TEST_INSTANCE_ID)
+ net_cfg = {
+ 'version': 1, 'config': [
+ {'subnets': [{'type': 'dhcp'}], 'type': 'physical',
+ 'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
+
+ def fake_network_config():
+ return net_cfg, 'fallback'
+
+ self.init._find_networking_config = fake_network_config
+ self.init.datasource = FakeDataSource(paths=self.init.paths)
+ self.init.datasource.update_events = {'network': [EventType.BOOT]}
+ self.init.apply_network_config(True)
+ self.init.distro.apply_network_config_names.assert_called_with(net_cfg)
+ self.init.distro.apply_network_config.assert_called_with(
+ net_cfg, bring_up=True)
+
+# vi: ts=4 expandtab
diff --git a/cloudinit/tests/test_util.py b/cloudinit/tests/test_util.py
index 17853fc7..6a31e505 100644
--- a/cloudinit/tests/test_util.py
+++ b/cloudinit/tests/test_util.py
@@ -26,8 +26,51 @@ OS_RELEASE_SLES = dedent("""\
CPE_NAME="cpe:/o:suse:sles:12:sp3"\n
""")
+OS_RELEASE_OPENSUSE = dedent("""\
+NAME="openSUSE Leap"
+VERSION="42.3"
+ID=opensuse
+ID_LIKE="suse"
+VERSION_ID="42.3"
+PRETTY_NAME="openSUSE Leap 42.3"
+ANSI_COLOR="0;32"
+CPE_NAME="cpe:/o:opensuse:leap:42.3"
+BUG_REPORT_URL="https://bugs.opensuse.org"
+HOME_URL="https://www.opensuse.org/"
+""")
+
+OS_RELEASE_CENTOS = dedent("""\
+ NAME="CentOS Linux"
+ VERSION="7 (Core)"
+ ID="centos"
+ ID_LIKE="rhel fedora"
+ VERSION_ID="7"
+ PRETTY_NAME="CentOS Linux 7 (Core)"
+ ANSI_COLOR="0;31"
+ CPE_NAME="cpe:/o:centos:centos:7"
+ HOME_URL="https://www.centos.org/"
+ BUG_REPORT_URL="https://bugs.centos.org/"
+
+ CENTOS_MANTISBT_PROJECT="CentOS-7"
+ CENTOS_MANTISBT_PROJECT_VERSION="7"
+ REDHAT_SUPPORT_PRODUCT="centos"
+ REDHAT_SUPPORT_PRODUCT_VERSION="7"
+""")
+
+OS_RELEASE_DEBIAN = dedent("""\
+ PRETTY_NAME="Debian GNU/Linux 9 (stretch)"
+ NAME="Debian GNU/Linux"
+ VERSION_ID="9"
+ VERSION="9 (stretch)"
+ ID=debian
+ HOME_URL="https://www.debian.org/"
+ SUPPORT_URL="https://www.debian.org/support"
+ BUG_REPORT_URL="https://bugs.debian.org/"
+""")
+
OS_RELEASE_UBUNTU = dedent("""\
NAME="Ubuntu"\n
+ # comment test
VERSION="16.04.3 LTS (Xenial Xerus)"\n
ID=ubuntu\n
ID_LIKE=debian\n
@@ -310,7 +353,31 @@ class TestGetLinuxDistro(CiTestCase):
m_os_release.return_value = OS_RELEASE_UBUNTU
m_path_exists.side_effect = TestGetLinuxDistro.os_release_exists
dist = util.get_linux_distro()
- self.assertEqual(('ubuntu', '16.04', platform.machine()), dist)
+ self.assertEqual(('ubuntu', '16.04', 'xenial'), dist)
+
+ @mock.patch('cloudinit.util.load_file')
+ def test_get_linux_centos(self, m_os_release, m_path_exists):
+ """Verify we get the correct name and release name on CentOS."""
+ m_os_release.return_value = OS_RELEASE_CENTOS
+ m_path_exists.side_effect = TestGetLinuxDistro.os_release_exists
+ dist = util.get_linux_distro()
+ self.assertEqual(('centos', '7', 'Core'), dist)
+
+ @mock.patch('cloudinit.util.load_file')
+ def test_get_linux_debian(self, m_os_release, m_path_exists):
+ """Verify we get the correct name and release name on Debian."""
+ m_os_release.return_value = OS_RELEASE_DEBIAN
+ m_path_exists.side_effect = TestGetLinuxDistro.os_release_exists
+ dist = util.get_linux_distro()
+ self.assertEqual(('debian', '9', 'stretch'), dist)
+
+ @mock.patch('cloudinit.util.load_file')
+ def test_get_linux_opensuse(self, m_os_release, m_path_exists):
+ """Verify we get the correct name and machine arch on OpenSUSE."""
+ m_os_release.return_value = OS_RELEASE_OPENSUSE
+ m_path_exists.side_effect = TestGetLinuxDistro.os_release_exists
+ dist = util.get_linux_distro()
+ self.assertEqual(('opensuse', '42.3', platform.machine()), dist)
@mock.patch('platform.dist')
def test_get_linux_distro_no_data(self, m_platform_dist, m_path_exists):