summaryrefslogtreecommitdiff
path: root/cloudinit/tests
diff options
context:
space:
mode:
Diffstat (limited to 'cloudinit/tests')
-rw-r--r--cloudinit/tests/helpers.py49
-rw-r--r--cloudinit/tests/test_dhclient_hook.py2
-rw-r--r--cloudinit/tests/test_gpg.py4
-rw-r--r--cloudinit/tests/test_netinfo.py14
-rw-r--r--cloudinit/tests/test_stages.py151
-rw-r--r--cloudinit/tests/test_temp_utils.py18
-rw-r--r--cloudinit/tests/test_url_helper.py53
-rw-r--r--cloudinit/tests/test_util.py60
-rw-r--r--cloudinit/tests/test_version.py4
9 files changed, 298 insertions, 57 deletions
diff --git a/cloudinit/tests/helpers.py b/cloudinit/tests/helpers.py
index 2eb7b0cd..70f6bad7 100644
--- a/cloudinit/tests/helpers.py
+++ b/cloudinit/tests/helpers.py
@@ -4,15 +4,17 @@ from __future__ import print_function
import functools
import httpretty
+import io
import logging
import os
+import random
import shutil
+import string
import sys
import tempfile
import time
+from unittest import mock
-import mock
-import six
import unittest2
from unittest2.util import strclass
@@ -41,26 +43,6 @@ _real_subp = util.subp
SkipTest = unittest2.SkipTest
skipIf = unittest2.skipIf
-# Used for detecting different python versions
-PY2 = False
-PY26 = False
-PY27 = False
-PY3 = False
-
-_PY_VER = sys.version_info
-_PY_MAJOR, _PY_MINOR, _PY_MICRO = _PY_VER[0:3]
-if (_PY_MAJOR, _PY_MINOR) <= (2, 6):
- if (_PY_MAJOR, _PY_MINOR) == (2, 6):
- PY26 = True
- if (_PY_MAJOR, _PY_MINOR) >= (2, 0):
- PY2 = True
-else:
- if (_PY_MAJOR, _PY_MINOR) == (2, 7):
- PY27 = True
- PY2 = True
- if (_PY_MAJOR, _PY_MINOR) >= (3, 0):
- PY3 = True
-
# Makes the old path start
# with new base instead of whatever
@@ -90,7 +72,7 @@ def retarget_many_wrapper(new_base, am, old_func):
# Python 3 some of these now accept file-descriptors (integers).
# That breaks rebase_path() so in lieu of a better solution, just
# don't rebase if we get a fd.
- if isinstance(path, six.string_types):
+ if isinstance(path, str):
n_args[i] = rebase_path(path, new_base)
return old_func(*n_args, **kwds)
return wrapper
@@ -167,7 +149,7 @@ class CiTestCase(TestCase):
if self.with_logs:
# Create a log handler so unit tests can search expected logs.
self.logger = logging.getLogger()
- self.logs = six.StringIO()
+ self.logs = io.StringIO()
formatter = logging.Formatter('%(levelname)s: %(message)s')
handler = logging.StreamHandler(self.logs)
handler.setFormatter(formatter)
@@ -184,7 +166,7 @@ class CiTestCase(TestCase):
else:
cmd = args[0]
- if not isinstance(cmd, six.string_types):
+ if not isinstance(cmd, str):
cmd = cmd[0]
pass_through = False
if not isinstance(self.allowed_subp, (list, bool)):
@@ -207,6 +189,7 @@ class CiTestCase(TestCase):
if self.with_logs:
# Remove the handler we setup
logging.getLogger().handlers = self.old_handlers
+ logging.getLogger().level = None
util.subp = _real_subp
super(CiTestCase, self).tearDown()
@@ -217,7 +200,8 @@ class CiTestCase(TestCase):
prefix="ci-%s." % self.__class__.__name__)
else:
tmpd = tempfile.mkdtemp(dir=dir)
- self.addCleanup(functools.partial(shutil.rmtree, tmpd))
+ self.addCleanup(
+ functools.partial(shutil.rmtree, tmpd, ignore_errors=True))
return tmpd
def tmp_path(self, path, dir=None):
@@ -261,6 +245,12 @@ class CiTestCase(TestCase):
myds.metadata.update(metadata)
return cloud.Cloud(myds, self.paths, sys_cfg, mydist, None)
+ @classmethod
+ def random_string(cls, length=8):
+ """ return a random lowercase string with default length of 8"""
+ return ''.join(
+ random.choice(string.ascii_lowercase) for _ in range(length))
+
class ResourceUsingTestCase(CiTestCase):
@@ -356,8 +346,9 @@ class FilesystemMockingTestCase(ResourceUsingTestCase):
def patchOpen(self, new_root):
trap_func = retarget_many_wrapper(new_root, 1, open)
- name = 'builtins.open' if PY3 else '__builtin__.open'
- self.patched_funcs.enter_context(mock.patch(name, trap_func))
+ self.patched_funcs.enter_context(
+ mock.patch('builtins.open', trap_func)
+ )
def patchStdoutAndStderr(self, stdout=None, stderr=None):
if stdout is not None:
@@ -430,7 +421,7 @@ def populate_dir(path, files):
p = os.path.sep.join([path, name])
util.ensure_dir(os.path.dirname(p))
with open(p, "wb") as fp:
- if isinstance(content, six.binary_type):
+ if isinstance(content, bytes):
fp.write(content)
else:
fp.write(content.encode('utf-8'))
diff --git a/cloudinit/tests/test_dhclient_hook.py b/cloudinit/tests/test_dhclient_hook.py
index 7aab8dd5..eadae81c 100644
--- a/cloudinit/tests/test_dhclient_hook.py
+++ b/cloudinit/tests/test_dhclient_hook.py
@@ -7,8 +7,8 @@ from cloudinit.tests.helpers import CiTestCase, dir2dict, populate_dir
import argparse
import json
-import mock
import os
+from unittest import mock
class TestDhclientHook(CiTestCase):
diff --git a/cloudinit/tests/test_gpg.py b/cloudinit/tests/test_gpg.py
index 0562b966..8dd57137 100644
--- a/cloudinit/tests/test_gpg.py
+++ b/cloudinit/tests/test_gpg.py
@@ -1,12 +1,12 @@
# This file is part of cloud-init. See LICENSE file for license information.
"""Test gpg module."""
+from unittest import mock
+
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")
diff --git a/cloudinit/tests/test_netinfo.py b/cloudinit/tests/test_netinfo.py
index d76e768e..1c8a791e 100644
--- a/cloudinit/tests/test_netinfo.py
+++ b/cloudinit/tests/test_netinfo.py
@@ -11,6 +11,7 @@ from cloudinit.tests.helpers import CiTestCase, mock, readResource
# Example ifconfig and route output
SAMPLE_OLD_IFCONFIG_OUT = readResource("netinfo/old-ifconfig-output")
SAMPLE_NEW_IFCONFIG_OUT = readResource("netinfo/new-ifconfig-output")
+SAMPLE_FREEBSD_IFCONFIG_OUT = readResource("netinfo/freebsd-ifconfig-output")
SAMPLE_IPADDRSHOW_OUT = readResource("netinfo/sample-ipaddrshow-output")
SAMPLE_ROUTE_OUT_V4 = readResource("netinfo/sample-route-output-v4")
SAMPLE_ROUTE_OUT_V6 = readResource("netinfo/sample-route-output-v6")
@@ -18,6 +19,7 @@ SAMPLE_IPROUTE_OUT_V4 = readResource("netinfo/sample-iproute-output-v4")
SAMPLE_IPROUTE_OUT_V6 = readResource("netinfo/sample-iproute-output-v6")
NETDEV_FORMATTED_OUT = readResource("netinfo/netdev-formatted-output")
ROUTE_FORMATTED_OUT = readResource("netinfo/route-formatted-output")
+FREEBSD_NETDEV_OUT = readResource("netinfo/freebsd-netdev-formatted-output")
class TestNetInfo(CiTestCase):
@@ -45,6 +47,18 @@ class TestNetInfo(CiTestCase):
@mock.patch('cloudinit.netinfo.util.which')
@mock.patch('cloudinit.netinfo.util.subp')
+ def test_netdev_freebsd_nettools_pformat(self, m_subp, m_which):
+ """netdev_pformat properly rendering netdev new nettools info."""
+ m_subp.return_value = (SAMPLE_FREEBSD_IFCONFIG_OUT, '')
+ m_which.side_effect = lambda x: x if x == 'ifconfig' else None
+ content = netdev_pformat()
+ print()
+ print(content)
+ print()
+ self.assertEqual(FREEBSD_NETDEV_OUT, content)
+
+ @mock.patch('cloudinit.netinfo.util.which')
+ @mock.patch('cloudinit.netinfo.util.subp')
def test_netdev_iproute_pformat(self, m_subp, m_which):
"""netdev_pformat properly rendering ip route info."""
m_subp.return_value = (SAMPLE_IPADDRSHOW_OUT, '')
diff --git a/cloudinit/tests/test_stages.py b/cloudinit/tests/test_stages.py
index 94b6b255..d5c9c0e4 100644
--- a/cloudinit/tests/test_stages.py
+++ b/cloudinit/tests/test_stages.py
@@ -6,6 +6,7 @@ import os
from cloudinit import stages
from cloudinit import sources
+from cloudinit.sources import NetworkConfigSource
from cloudinit.event import EventType
from cloudinit.util import write_file
@@ -37,6 +38,7 @@ class FakeDataSource(sources.DataSource):
class TestInit(CiTestCase):
with_logs = True
+ allowed_subp = False
def setUp(self):
super(TestInit, self).setUp()
@@ -57,84 +59,189 @@ class TestInit(CiTestCase):
(None, disable_file),
self.init._find_networking_config())
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
- def test_wb__find_networking_config_disabled_by_kernel(self, m_cmdline):
+ def test_wb__find_networking_config_disabled_by_kernel(
+ self, m_cmdline, m_initramfs):
"""find_networking_config returns when disabled by kernel cmdline."""
m_cmdline.return_value = {'config': 'disabled'}
+ m_initramfs.return_value = {'config': ['fake_initrd']}
self.assertEqual(
- (None, 'cmdline'),
+ (None, NetworkConfigSource.cmdline),
self.init._find_networking_config())
self.assertEqual('DEBUG: network config disabled by cmdline\n',
self.logs.getvalue())
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
- def test_wb__find_networking_config_disabled_by_datasrc(self, m_cmdline):
+ def test_wb__find_networking_config_disabled_by_initrd(
+ self, m_cmdline, m_initramfs):
+ """find_networking_config returns when disabled by kernel cmdline."""
+ m_cmdline.return_value = {}
+ m_initramfs.return_value = {'config': 'disabled'}
+ self.assertEqual(
+ (None, NetworkConfigSource.initramfs),
+ self.init._find_networking_config())
+ self.assertEqual('DEBUG: network config disabled by initramfs\n',
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_disabled_by_datasrc(
+ self, m_cmdline, m_initramfs):
"""find_networking_config returns when disabled by datasource cfg."""
m_cmdline.return_value = {} # Kernel doesn't disable networking
+ m_initramfs.return_value = {} # initramfs 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'),
+ (None, NetworkConfigSource.ds),
self.init._find_networking_config())
self.assertEqual('DEBUG: network config disabled by ds\n',
self.logs.getvalue())
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
- def test_wb__find_networking_config_disabled_by_sysconfig(self, m_cmdline):
+ def test_wb__find_networking_config_disabled_by_sysconfig(
+ self, m_cmdline, m_initramfs):
"""find_networking_config returns when disabled by system config."""
m_cmdline.return_value = {} # Kernel doesn't disable networking
+ m_initramfs.return_value = {} # initramfs doesn't disable networking
self.init._cfg = {'system_info': {'paths': {'cloud_dir': self.tmpdir}},
'network': {'config': 'disabled'}}
self.assertEqual(
- (None, 'system_cfg'),
+ (None, NetworkConfigSource.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_initramfs_config')
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test__find_networking_config_uses_datasrc_order(
+ self, m_cmdline, m_initramfs):
+ """find_networking_config should check sources in DS defined order"""
+ # cmdline and initramfs, which would normally be preferred over other
+ # sources, disable networking; in this case, though, the DS moves them
+ # later so its own config is preferred
+ m_cmdline.return_value = {'config': 'disabled'}
+ m_initramfs.return_value = {'config': 'disabled'}
+
+ ds_net_cfg = {'config': {'needle': True}}
+ self.init.datasource = FakeDataSource(network_config=ds_net_cfg)
+ self.init.datasource.network_config_sources = [
+ NetworkConfigSource.ds, NetworkConfigSource.system_cfg,
+ NetworkConfigSource.cmdline, NetworkConfigSource.initramfs]
+
+ self.assertEqual(
+ (ds_net_cfg, NetworkConfigSource.ds),
+ self.init._find_networking_config())
+
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test__find_networking_config_warns_if_datasrc_uses_invalid_src(
+ self, m_cmdline, m_initramfs):
+ """find_networking_config should check sources in DS defined order"""
+ ds_net_cfg = {'config': {'needle': True}}
+ self.init.datasource = FakeDataSource(network_config=ds_net_cfg)
+ self.init.datasource.network_config_sources = [
+ 'invalid_src', NetworkConfigSource.ds]
+
+ self.assertEqual(
+ (ds_net_cfg, NetworkConfigSource.ds),
+ self.init._find_networking_config())
+ self.assertIn('WARNING: data source specifies an invalid network'
+ ' cfg_source: invalid_src',
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
- def test_wb__find_networking_config_returns_kernel(self, m_cmdline):
+ def test__find_networking_config_warns_if_datasrc_uses_unavailable_src(
+ self, m_cmdline, m_initramfs):
+ """find_networking_config should check sources in DS defined order"""
+ ds_net_cfg = {'config': {'needle': True}}
+ self.init.datasource = FakeDataSource(network_config=ds_net_cfg)
+ self.init.datasource.network_config_sources = [
+ NetworkConfigSource.fallback, NetworkConfigSource.ds]
+
+ self.assertEqual(
+ (ds_net_cfg, NetworkConfigSource.ds),
+ self.init._find_networking_config())
+ self.assertIn('WARNING: data source specifies an unavailable network'
+ ' cfg_source: fallback',
+ self.logs.getvalue())
+
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_returns_kernel(
+ self, m_cmdline, m_initramfs):
"""find_networking_config returns kernel cmdline config if present."""
expected_cfg = {'config': ['fakekernel']}
m_cmdline.return_value = expected_cfg
+ m_initramfs.return_value = {'config': ['fake_initrd']}
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'),
+ (expected_cfg, NetworkConfigSource.cmdline),
self.init._find_networking_config())
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
- def test_wb__find_networking_config_returns_system_cfg(self, m_cmdline):
+ def test_wb__find_networking_config_returns_initramfs(
+ self, m_cmdline, m_initramfs):
+ """find_networking_config returns kernel cmdline config if present."""
+ expected_cfg = {'config': ['fake_initrd']}
+ m_cmdline.return_value = {}
+ m_initramfs.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, NetworkConfigSource.initramfs),
+ self.init._find_networking_config())
+
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
+ @mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
+ def test_wb__find_networking_config_returns_system_cfg(
+ self, m_cmdline, m_initramfs):
"""find_networking_config returns system config when present."""
m_cmdline.return_value = {} # No kernel network config
+ m_initramfs.return_value = {} # no initramfs 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'),
+ (expected_cfg, NetworkConfigSource.system_cfg),
self.init._find_networking_config())
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
- def test_wb__find_networking_config_returns_datasrc_cfg(self, m_cmdline):
+ def test_wb__find_networking_config_returns_datasrc_cfg(
+ self, m_cmdline, m_initramfs):
"""find_networking_config returns datasource net config if present."""
m_cmdline.return_value = {} # No kernel network config
+ m_initramfs.return_value = {} # no initramfs 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'),
+ (expected_cfg, NetworkConfigSource.ds),
self.init._find_networking_config())
+ @mock.patch('cloudinit.stages.cmdline.read_initramfs_config')
@mock.patch('cloudinit.stages.cmdline.read_kernel_cmdline_config')
- def test_wb__find_networking_config_returns_fallback(self, m_cmdline):
+ def test_wb__find_networking_config_returns_fallback(
+ self, m_cmdline, m_initramfs):
"""find_networking_config returns fallback config if not defined."""
m_cmdline.return_value = {} # Kernel doesn't disable networking
+ m_initramfs.return_value = {} # no initramfs network config
# Neither datasource nor system_info disable or provide network
fake_cfg = {'config': [{'type': 'physical', 'name': 'eth9'}],
@@ -147,7 +254,7 @@ class TestInit(CiTestCase):
distro = self.init.distro
distro.generate_fallback_config = fake_generate_fallback
self.assertEqual(
- (fake_cfg, 'fallback'),
+ (fake_cfg, NetworkConfigSource.fallback),
self.init._find_networking_config())
self.assertNotIn('network config disabled', self.logs.getvalue())
@@ -166,8 +273,9 @@ class TestInit(CiTestCase):
'INFO: network config is disabled by %s' % disable_file,
self.logs.getvalue())
+ @mock.patch('cloudinit.net.get_interfaces_by_mac')
@mock.patch('cloudinit.distros.ubuntu.Distro')
- def test_apply_network_on_new_instance(self, m_ubuntu):
+ def test_apply_network_on_new_instance(self, m_ubuntu, m_macs):
"""Call distro apply_network_config methods on is_new_instance."""
net_cfg = {
'version': 1, 'config': [
@@ -175,7 +283,9 @@ class TestInit(CiTestCase):
'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
def fake_network_config():
- return net_cfg, 'fallback'
+ return net_cfg, NetworkConfigSource.fallback
+
+ m_macs.return_value = {'42:42:42:42:42:42': 'eth9'}
self.init._find_networking_config = fake_network_config
self.init.apply_network_config(True)
@@ -195,7 +305,7 @@ class TestInit(CiTestCase):
'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
def fake_network_config():
- return net_cfg, 'fallback'
+ return net_cfg, NetworkConfigSource.fallback
self.init._find_networking_config = fake_network_config
self.init.apply_network_config(True)
@@ -206,8 +316,9 @@ class TestInit(CiTestCase):
" nor datasource network update on '%s' event" % EventType.BOOT,
self.logs.getvalue())
+ @mock.patch('cloudinit.net.get_interfaces_by_mac')
@mock.patch('cloudinit.distros.ubuntu.Distro')
- def test_apply_network_on_datasource_allowed_event(self, m_ubuntu):
+ def test_apply_network_on_datasource_allowed_event(self, m_ubuntu, m_macs):
"""Apply network if datasource.update_metadata permits BOOT event."""
old_instance_id = os.path.join(
self.init.paths.get_cpath('data'), 'instance-id')
@@ -218,7 +329,9 @@ class TestInit(CiTestCase):
'name': 'eth9', 'mac_address': '42:42:42:42:42:42'}]}
def fake_network_config():
- return net_cfg, 'fallback'
+ return net_cfg, NetworkConfigSource.fallback
+
+ m_macs.return_value = {'42:42:42:42:42:42': 'eth9'}
self.init._find_networking_config = fake_network_config
self.init.datasource = FakeDataSource(paths=self.init.paths)
diff --git a/cloudinit/tests/test_temp_utils.py b/cloudinit/tests/test_temp_utils.py
index ffbb92cd..4a52ef89 100644
--- a/cloudinit/tests/test_temp_utils.py
+++ b/cloudinit/tests/test_temp_utils.py
@@ -2,8 +2,9 @@
"""Tests for cloudinit.temp_utils"""
-from cloudinit.temp_utils import mkdtemp, mkstemp
+from cloudinit.temp_utils import mkdtemp, mkstemp, tempdir
from cloudinit.tests.helpers import CiTestCase, wrap_and_call
+import os
class TestTempUtils(CiTestCase):
@@ -98,4 +99,19 @@ class TestTempUtils(CiTestCase):
self.assertEqual('/fake/return/path', retval)
self.assertEqual([{'dir': '/run/cloud-init/tmp'}], calls)
+ def test_tempdir_error_suppression(self):
+ """test tempdir suppresses errors during directory removal."""
+
+ with self.assertRaises(OSError):
+ with tempdir(prefix='cloud-init-dhcp-') as tdir:
+ os.rmdir(tdir)
+ # As a result, the directory is already gone,
+ # so shutil.rmtree should raise OSError
+
+ with tempdir(rmtree_ignore_errors=True,
+ prefix='cloud-init-dhcp-') as tdir:
+ os.rmdir(tdir)
+ # Since the directory is already gone, shutil.rmtree would raise
+ # OSError, but we suppress that
+
# vi: ts=4 expandtab
diff --git a/cloudinit/tests/test_url_helper.py b/cloudinit/tests/test_url_helper.py
index aa9f3ec1..1674120f 100644
--- a/cloudinit/tests/test_url_helper.py
+++ b/cloudinit/tests/test_url_helper.py
@@ -4,6 +4,7 @@ from cloudinit.url_helper import (
NOT_FOUND, UrlError, oauth_headers, read_file_or_url, retry_on_url_exc)
from cloudinit.tests.helpers import CiTestCase, mock, skipIf
from cloudinit import util
+from cloudinit import version
import httpretty
import requests
@@ -17,6 +18,9 @@ except ImportError:
_missing_oauthlib_dep = True
+M_PATH = 'cloudinit.url_helper.'
+
+
class TestOAuthHeaders(CiTestCase):
def test_oauth_headers_raises_not_implemented_when_oathlib_missing(self):
@@ -67,6 +71,55 @@ class TestReadFileOrUrl(CiTestCase):
self.assertEqual(result.contents, data)
self.assertEqual(str(result), data.decode('utf-8'))
+ @mock.patch(M_PATH + 'readurl')
+ def test_read_file_or_url_passes_params_to_readurl(self, m_readurl):
+ """read_file_or_url passes all params through to readurl."""
+ url = 'http://hostname/path'
+ response = 'This is my url content\n'
+ m_readurl.return_value = response
+ params = {'url': url, 'timeout': 1, 'retries': 2,
+ 'headers': {'somehdr': 'val'},
+ 'data': 'data', 'sec_between': 1,
+ 'ssl_details': {'cert_file': '/path/cert.pem'},
+ 'headers_cb': 'headers_cb', 'exception_cb': 'exception_cb'}
+ self.assertEqual(response, read_file_or_url(**params))
+ params.pop('url') # url is passed in as a positional arg
+ self.assertEqual([mock.call(url, **params)], m_readurl.call_args_list)
+
+ def test_wb_read_url_defaults_honored_by_read_file_or_url_callers(self):
+ """Readurl param defaults used when unspecified by read_file_or_url
+
+ Param defaults tested are as follows:
+ retries: 0, additional headers None beyond default, method: GET,
+ data: None, check_status: True and allow_redirects: True
+ """
+ url = 'http://hostname/path'
+
+ m_response = mock.MagicMock()
+
+ class FakeSession(requests.Session):
+ @classmethod
+ def request(cls, **kwargs):
+ self.assertEqual(
+ {'url': url, 'allow_redirects': True, 'method': 'GET',
+ 'headers': {
+ 'User-Agent': 'Cloud-Init/%s' % (
+ version.version_string())}},
+ kwargs)
+ return m_response
+
+ with mock.patch(M_PATH + 'requests.Session') as m_session:
+ error = requests.exceptions.HTTPError('broke')
+ m_session.side_effect = [error, FakeSession()]
+ # assert no retries and check_status == True
+ with self.assertRaises(UrlError) as context_manager:
+ response = read_file_or_url(url)
+ self.assertEqual('broke', str(context_manager.exception))
+ # assert default headers, method, url and allow_redirects True
+ # Success on 2nd call with FakeSession
+ response = read_file_or_url(url)
+ self.assertEqual(m_response, response._response)
+
class TestRetryOnUrlExc(CiTestCase):
diff --git a/cloudinit/tests/test_util.py b/cloudinit/tests/test_util.py
index e3d2dbaa..11f37000 100644
--- a/cloudinit/tests/test_util.py
+++ b/cloudinit/tests/test_util.py
@@ -2,7 +2,9 @@
"""Tests for cloudinit.util"""
+import base64
import logging
+import json
import platform
import cloudinit.util as util
@@ -187,6 +189,21 @@ class TestUtil(CiTestCase):
self.assertEqual(is_rw, False)
+class TestUptime(CiTestCase):
+
+ @mock.patch('cloudinit.util.boottime')
+ @mock.patch('cloudinit.util.os.path.exists')
+ @mock.patch('cloudinit.util.time.time')
+ def test_uptime_non_linux_path(self, m_time, m_exists, m_boottime):
+ boottime = 1000.0
+ uptime = 10.0
+ m_boottime.return_value = boottime
+ m_time.return_value = boottime + uptime
+ m_exists.return_value = False
+ result = util.uptime()
+ self.assertEqual(str(uptime), result)
+
+
class TestShellify(CiTestCase):
def test_input_dict_raises_type_error(self):
@@ -385,6 +402,11 @@ class TestUdevadmSettle(CiTestCase):
@mock.patch('os.path.exists')
class TestGetLinuxDistro(CiTestCase):
+ def setUp(self):
+ # python2 has no lru_cache, and therefore, no cache_clear()
+ if hasattr(util.get_linux_distro, "cache_clear"):
+ util.get_linux_distro.cache_clear()
+
@classmethod
def os_release_exists(self, path):
"""Side effect function"""
@@ -397,6 +419,12 @@ class TestGetLinuxDistro(CiTestCase):
if path == '/etc/redhat-release':
return 1
+ @classmethod
+ def freebsd_version_exists(self, path):
+ """Side effect function """
+ if path == '/bin/freebsd-version':
+ return 1
+
@mock.patch('cloudinit.util.load_file')
def test_get_linux_distro_quoted_name(self, m_os_release, m_path_exists):
"""Verify we get the correct name if the os-release file has
@@ -415,6 +443,14 @@ class TestGetLinuxDistro(CiTestCase):
dist = util.get_linux_distro()
self.assertEqual(('ubuntu', '16.04', 'xenial'), dist)
+ @mock.patch('cloudinit.util.subp')
+ def test_get_linux_freebsd(self, m_subp, m_path_exists):
+ """Verify we get the correct name and release name on FreeBSD."""
+ m_path_exists.side_effect = TestGetLinuxDistro.freebsd_version_exists
+ m_subp.return_value = ("12.0-RELEASE-p10\n", '')
+ dist = util.get_linux_distro()
+ self.assertEqual(('freebsd', '12.0-RELEASE-p10', ''), dist)
+
@mock.patch('cloudinit.util.load_file')
def test_get_linux_centos6(self, m_os_release, m_path_exists):
"""Verify we get the correct name and release name on CentOS 6."""
@@ -502,7 +538,7 @@ class TestGetLinuxDistro(CiTestCase):
self.assertEqual(
('opensuse-tumbleweed', '20180920', platform.machine()), dist)
- @mock.patch('platform.dist')
+ @mock.patch('platform.dist', create=True)
def test_get_linux_distro_no_data(self, m_platform_dist, m_path_exists):
"""Verify we get no information if os-release does not exist"""
m_platform_dist.return_value = ('', '', '')
@@ -510,7 +546,7 @@ class TestGetLinuxDistro(CiTestCase):
dist = util.get_linux_distro()
self.assertEqual(('', '', ''), dist)
- @mock.patch('platform.dist')
+ @mock.patch('platform.dist', create=True)
def test_get_linux_distro_no_impl(self, m_platform_dist, m_path_exists):
"""Verify we get an empty tuple when no information exists and
Exceptions are not propagated"""
@@ -519,7 +555,7 @@ class TestGetLinuxDistro(CiTestCase):
dist = util.get_linux_distro()
self.assertEqual(('', '', ''), dist)
- @mock.patch('platform.dist')
+ @mock.patch('platform.dist', create=True)
def test_get_linux_distro_plat_data(self, m_platform_dist, m_path_exists):
"""Verify we get the correct platform information"""
m_platform_dist.return_value = ('foo', '1.1', 'aarch64')
@@ -528,6 +564,24 @@ class TestGetLinuxDistro(CiTestCase):
self.assertEqual(('foo', '1.1', 'aarch64'), dist)
+class TestJsonDumps(CiTestCase):
+ def test_is_str(self):
+ """json_dumps should return a string."""
+ self.assertTrue(isinstance(util.json_dumps({'abc': '123'}), str))
+
+ def test_utf8(self):
+ smiley = '\\ud83d\\ude03'
+ self.assertEqual(
+ {'smiley': smiley},
+ json.loads(util.json_dumps({'smiley': smiley})))
+
+ def test_non_utf8(self):
+ blob = b'\xba\x03Qx-#y\xea'
+ self.assertEqual(
+ {'blob': 'ci-b64:' + base64.b64encode(blob).decode('utf-8')},
+ json.loads(util.json_dumps({'blob': blob})))
+
+
@mock.patch('os.path.exists')
class TestIsLXD(CiTestCase):
diff --git a/cloudinit/tests/test_version.py b/cloudinit/tests/test_version.py
index a96c2a47..778a762c 100644
--- a/cloudinit/tests/test_version.py
+++ b/cloudinit/tests/test_version.py
@@ -1,10 +1,10 @@
# This file is part of cloud-init. See LICENSE file for license information.
+from unittest import mock
+
from cloudinit.tests.helpers import CiTestCase
from cloudinit import version
-import mock
-
class TestExportsFeatures(CiTestCase):
def test_has_network_config_v1(self):