From 3cb8ecc229999dbe524ff2ba4c4bd693e3c66058 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 10 Oct 2014 17:27:56 -0700 Subject: Increase the robustness of the chef module Add the following adjustments to the chef template and module - Make it so that the chef directories can be provided (defaults to the existing directories) - Make the params much more configurable, and if a parameter is provided in the chef configuration it will override existing template parameters. - Make the template skip lines if the values are None in the configuration so that template lines can be removed if/when this is desirable. - Allow the firstboot json path to be configurable (defaults to the existing location). - Adds a basic set of tests to ensure that good things are happening. --- tests/unittests/test_handler/test_handler_chef.py | 84 +++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 tests/unittests/test_handler/test_handler_chef.py (limited to 'tests') diff --git a/tests/unittests/test_handler/test_handler_chef.py b/tests/unittests/test_handler/test_handler_chef.py new file mode 100644 index 00000000..5562d18a --- /dev/null +++ b/tests/unittests/test_handler/test_handler_chef.py @@ -0,0 +1,84 @@ +import os +import json + +from cloudinit.config import cc_chef + +from cloudinit import cloud +from cloudinit import distros +from cloudinit import helpers +from cloudinit import util +from cloudinit.sources import DataSourceNone + +from .. import helpers as t_help + +import logging + +LOG = logging.getLogger(__name__) + + +class TestChef(t_help.FilesystemMockingTestCase): + def setUp(self): + super(TestChef, self).setUp() + self.tmp = self.makeDir(prefix="unittest_") + + def fetch_cloud(self, distro_kind): + cls = distros.fetch(distro_kind) + paths = helpers.Paths({}) + distro = cls(distro_kind, {}, paths) + ds = DataSourceNone.DataSourceNone({}, distro, paths, None) + return cloud.Cloud(ds, paths, {}, distro, None) + + def test_no_config(self): + self.patchUtils(self.tmp) + self.patchOS(self.tmp) + + cfg = {} + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) + for d in cc_chef.CHEF_DIRS: + self.assertFalse(os.path.isdir(d)) + + def test_basic_config(self): + tpl_file = util.load_file('templates/chef_client.rb.tmpl') + self.patchUtils(self.tmp) + self.patchOS(self.tmp) + + util.write_file('/etc/cloud/templates/chef_client.rb.tmpl', tpl_file) + cfg = { + 'chef': { + 'server_url': 'localhost', + 'validation_name': 'bob', + }, + } + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) + for d in cc_chef.CHEF_DIRS: + self.assertTrue(os.path.isdir(d)) + c = util.load_file(cc_chef.CHEF_RB_PATH) + for k, v in cfg['chef'].items(): + self.assertIn(v, c) + for k, v in cc_chef.CHEF_RB_TPL_DEFAULTS.items(): + if isinstance(v, basestring): + self.assertIn(v, c) + c = util.load_file(cc_chef.CHEF_FB_PATH) + self.assertEqual({}, json.loads(c)) + + def test_firstboot_json(self): + self.patchUtils(self.tmp) + self.patchOS(self.tmp) + + cfg = { + 'chef': { + 'server_url': 'localhost', + 'validation_name': 'bob', + 'run_list': ['a', 'b', 'c'], + 'initial_attributes': { + 'c': 'd', + } + }, + } + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) + c = util.load_file(cc_chef.CHEF_FB_PATH) + self.assertEqual( + { + 'run_list': ['a', 'b', 'c'], + 'c': 'd', + }, json.loads(c)) -- cgit v1.2.3 From 28d09d73651772ed6c95e67f24e0a04488e79bd5 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 10 Oct 2014 17:44:57 -0700 Subject: Add a few template delete tests --- tests/unittests/test_handler/test_handler_chef.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'tests') diff --git a/tests/unittests/test_handler/test_handler_chef.py b/tests/unittests/test_handler/test_handler_chef.py index 5562d18a..de7ff2da 100644 --- a/tests/unittests/test_handler/test_handler_chef.py +++ b/tests/unittests/test_handler/test_handler_chef.py @@ -82,3 +82,22 @@ class TestChef(t_help.FilesystemMockingTestCase): 'run_list': ['a', 'b', 'c'], 'c': 'd', }, json.loads(c)) + + def test_template_deletes(self): + tpl_file = util.load_file('templates/chef_client.rb.tmpl') + self.patchUtils(self.tmp) + self.patchOS(self.tmp) + + util.write_file('/etc/cloud/templates/chef_client.rb.tmpl', tpl_file) + cfg = { + 'chef': { + 'server_url': 'localhost', + 'validation_name': 'bob', + 'json_attribs': None, + 'show_time': None, + }, + } + cc_chef.handle('chef', cfg, self.fetch_cloud('ubuntu'), LOG, []) + c = util.load_file(cc_chef.CHEF_RB_PATH) + self.assertNotIn('json_attribs', c) + self.assertNotIn('Formatter.show_time', c) -- cgit v1.2.3 From 36bc6de57a2c9be1efe9ae7e9ebaf7fa9023d230 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 10 Oct 2014 18:54:28 -0700 Subject: Fix some of the erroring out test cases There are a couple new test cases that are now erroring out. - A usage of the non helper unit testcase base which is trigging and error on python 2.6 due to lack of method, fix this by using the base helper class. - A freebsd distro test check thats looking for /etc/resolv.conf and examining its contents, which won't exist due to our mocking routine that does not allow that file to be read. - A freebsd distro test where the distro class tries to call into ['ifconfig', '-a'] for a set of values, those values don't exist on the machine I am running on (and likely others machines) so we should mock the subp function out (that the distro class calls) and correctly return values that will work for the testcase. --- tests/unittests/test_datasource/test_openstack.py | 2 +- tests/unittests/test_distros/test_netconfig.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/unittests/test_datasource/test_openstack.py b/tests/unittests/test_datasource/test_openstack.py index 7b4e651a..8becbdd2 100644 --- a/tests/unittests/test_datasource/test_openstack.py +++ b/tests/unittests/test_datasource/test_openstack.py @@ -318,7 +318,7 @@ class TestOpenStackDataSource(test_helpers.HttprettyTestCase): self.assertIsNone(ds_os.version) -class TestVendorDataLoading(unittest.TestCase): +class TestVendorDataLoading(test_helpers.TestCase): def cvj(self, data): return openstack.convert_vendordata_json(data) diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py index fbdb7b3f..35cc1f43 100644 --- a/tests/unittests/test_distros/test_netconfig.py +++ b/tests/unittests/test_distros/test_netconfig.py @@ -182,6 +182,12 @@ NETWORKING=yes spec=False, passthrough=False) load_mock = self.mocker.replace(util.load_file, spec=False, passthrough=False) + subp_mock = self.mocker.replace(util.subp, + spec=False, passthrough=False) + + subp_mock(['ifconfig', '-a']) + self.mocker.count(0, None) + self.mocker.result(('vtnet0', '')) exists_mock(mocker.ARGS) self.mocker.count(0, None) @@ -190,6 +196,7 @@ NETWORKING=yes write_bufs = {} read_bufs = { '/etc/rc.conf': '', + '/etc/resolv.conf': '', } def replace_write(filename, content, mode=0644, omode="wb"): -- cgit v1.2.3 From d87e89d9c674bac7e87d483037850a9ee4fc984a Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Sat, 11 Oct 2014 16:59:50 -0700 Subject: More adjustments - Use the generated_by() utility function to give the ruby template a better header comment - Set special parameters after selecting the basic chef parameters. --- cloudinit/config/cc_chef.py | 19 +++++++++++-------- templates/chef_client.rb.tmpl | 3 +-- tests/unittests/test_handler/test_handler_chef.py | 20 +++++++++++++++++++- 3 files changed, 31 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/cloudinit/config/cc_chef.py b/cloudinit/config/cc_chef.py index fb825404..999b658d 100644 --- a/cloudinit/config/cc_chef.py +++ b/cloudinit/config/cc_chef.py @@ -79,13 +79,6 @@ def is_installed(): def get_template_params(iid, chef_cfg, log): params = CHEF_RB_TPL_DEFAULTS.copy() - params.update({ - 'server_url': chef_cfg['server_url'], - 'node_name': util.get_cfg_option_str(chef_cfg, 'node_name', iid), - 'environment': util.get_cfg_option_str(chef_cfg, 'environment', - '_default'), - 'validation_name': chef_cfg['validation_name'], - }) # Allow users to overwrite any of the keys they want (if they so choose), # when a value is None, then the value will be set to None and no boolean # or string version will be populated... @@ -101,7 +94,17 @@ def get_template_params(iid, chef_cfg, log): params[k] = util.get_cfg_option_bool(chef_cfg, k) else: params[k] = util.get_cfg_option_str(chef_cfg, k) - params['generated_on'] = datetime.now().isoformat() + # These ones are overwritten to be exact values... + params.update({ + 'generated_by': util.make_header(), + 'server_url': util.get_cfg_option_str(chef_cfg, 'server_url'), + 'node_name': util.get_cfg_option_str(chef_cfg, 'node_name', + default=iid), + 'environment': util.get_cfg_option_str(chef_cfg, 'environment', + default='_default'), + 'validation_name': util.get_cfg_option_str(chef_cfg, + 'validation_name'), + }) return params diff --git a/templates/chef_client.rb.tmpl b/templates/chef_client.rb.tmpl index 7b9e6298..c4069d22 100644 --- a/templates/chef_client.rb.tmpl +++ b/templates/chef_client.rb.tmpl @@ -9,12 +9,11 @@ you need to add the following to config: validation_name: XYZ server_url: XYZ -#} - +{{generated_by}} {# The reason these are not in quotes is because they are ruby symbols that will be placed inside here, and not actual strings... #} -# This is a generated file, created on {{generated_on}}. {% if log_level %} log_level {{log_level}} {% endif %} diff --git a/tests/unittests/test_handler/test_handler_chef.py b/tests/unittests/test_handler/test_handler_chef.py index de7ff2da..ef1aa208 100644 --- a/tests/unittests/test_handler/test_handler_chef.py +++ b/tests/unittests/test_handler/test_handler_chef.py @@ -1,5 +1,5 @@ -import os import json +import os from cloudinit.config import cc_chef @@ -38,6 +38,24 @@ class TestChef(t_help.FilesystemMockingTestCase): self.assertFalse(os.path.isdir(d)) def test_basic_config(self): + # This should create a file of the format... + """ + # Created by cloud-init v. 0.7.6 on Sat, 11 Oct 2014 23:57:21 +0000 + log_level :info + ssl_verify_mode :verify_none + log_location "/var/log/chef/client.log" + validation_client_name "bob" + validation_key "/etc/chef/validation.pem" + client_key "/etc/chef/client.pem" + chef_server_url "localhost" + environment "_default" + node_name "iid-datasource-none" + json_attribs "/etc/chef/firstboot.json" + file_cache_path "/var/cache/chef" + file_backup_path "/var/backups/chef" + pid_file "/var/run/chef/client.pid" + Chef::Log::Formatter.show_time = true + """ tpl_file = util.load_file('templates/chef_client.rb.tmpl') self.patchUtils(self.tmp) self.patchOS(self.tmp) -- cgit v1.2.3 From 4f24ed06e2ebfa59349fa8652b59f7743a78e79b Mon Sep 17 00:00:00 2001 From: Neal Shrader Date: Thu, 16 Oct 2014 17:53:59 -0400 Subject: Add tests for DigitalOcean datasource --- .../unittests/test_datasource/test_digitalocean.py | 95 ++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/unittests/test_datasource/test_digitalocean.py (limited to 'tests') diff --git a/tests/unittests/test_datasource/test_digitalocean.py b/tests/unittests/test_datasource/test_digitalocean.py new file mode 100644 index 00000000..9576e042 --- /dev/null +++ b/tests/unittests/test_datasource/test_digitalocean.py @@ -0,0 +1,95 @@ +# +# Copyright (C) 2014 Neal Shrader +# +# Author: Neal Shrader +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import httpretty +import re + +from urlparse import urlparse + +from cloudinit import settings +from cloudinit import helpers +from cloudinit.sources import DataSourceDigitalOcean + +from .. import helpers as test_helpers + +DO_META = { + 'user-data': '#!/bin/bash\necho "user-data"', + 'vendor-data': '#!/bin/bash\necho "vendor-data"', + 'public-keys': 'ssh-rsa AAAAB3NzaC1yc2EAAAA... neal@digitalocean.com', + 'region': 'nyc3', + 'id': '2000000', + 'hostname': 'cloudinit-test', +} + +MD_URL_RE = re.compile(r'http://169.254.169.254/metadata/v1/.*') + +def _request_callback(method, uri, headers): + url_path = urlparse(uri).path + if url_path.startswith('/metadata/v1/'): + path = url_path.split('/metadata/v1/')[1:][0] + else: + path = None + if path in DO_META: + return (200, headers, DO_META.get(path)) + else: + return (404, headers, '') + + +class TestDataSourceDigitalOcean(test_helpers.HttprettyTestCase): + + def setUp(self): + self.ds = DataSourceDigitalOcean.DataSourceDigitalOcean( + settings.CFG_BUILTIN, None, + helpers.Paths({})) + super(TestDataSourceDigitalOcean, self).setUp() + + @httpretty.activate + def test_connection(self): + httpretty.register_uri( + httpretty.GET, MD_URL_RE, + body=_request_callback) + + success = self.ds.get_data() + self.assertTrue(success) + + @httpretty.activate + def test_metadata(self): + httpretty.register_uri( + httpretty.GET, MD_URL_RE, + body=_request_callback) + self.ds.get_data() + + self.assertEqual(DO_META.get('user-data'), + self.ds.get_userdata_raw()) + + self.assertEqual(DO_META.get('vendor-data'), + self.ds.get_vendordata_raw()) + + self.assertEqual([DO_META.get('public-keys')], + self.ds.get_public_ssh_keys()) + + self.assertEqual(DO_META.get('region'), + self.ds.availability_zone) + + self.assertEqual(DO_META.get('id'), + self.ds.get_instance_id()) + + self.assertEqual(DO_META.get('hostname'), + self.ds.get_hostname()) + + self.assertEqual('http://mirrors.digitalocean.com/', + self.ds.get_package_mirror_info()) -- cgit v1.2.3 From 01e8df0557098093a0e3444f41ba3f1861ded316 Mon Sep 17 00:00:00 2001 From: Neal Shrader Date: Thu, 16 Oct 2014 19:19:29 -0400 Subject: Use existing metadata crawler to populate datasource --- cloudinit/sources/DataSourceDigitalOcean.py | 53 +++++++++------------- .../unittests/test_datasource/test_digitalocean.py | 9 ++++ 2 files changed, 30 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/cloudinit/sources/DataSourceDigitalOcean.py b/cloudinit/sources/DataSourceDigitalOcean.py index 985f1663..b7afca93 100644 --- a/cloudinit/sources/DataSourceDigitalOcean.py +++ b/cloudinit/sources/DataSourceDigitalOcean.py @@ -18,11 +18,14 @@ from cloudinit import log as logging from cloudinit import util from cloudinit import sources from cloudinit import url_helper +from cloudinit import ec2_utils +import functools + LOG = logging.getLogger(__name__) BUILTIN_DS_CONFIG = { - 'metadata_url': 'http://169.254.169.254/metadata/v1', + 'metadata_url': 'http://169.254.169.254/metadata/v1/', 'mirrors_url': 'http://mirrors.digitalocean.com/' } MD_RETRIES = 0 @@ -37,9 +40,9 @@ class DataSourceDigitalOcean(sources.DataSource): BUILTIN_DS_CONFIG]) self.metadata_address = self.ds_cfg['metadata_url'] - if self.ds_cfg.get('retries'): + if self.ds_cfg.get('retries'): self.retries = self.ds_cfg['retries'] - else: + else: self.retries = MD_RETRIES if self.ds_cfg.get('timeout'): @@ -48,41 +51,27 @@ class DataSourceDigitalOcean(sources.DataSource): self.timeout = MD_TIMEOUT def get_data(self): - url_map = [ - ('user-data', '/user-data'), - ('vendor-data', '/vendor-data'), - ('public-keys', '/public-keys'), - ('region', '/region'), - ('id', '/id'), - ('hostname', '/hostname'), - ] - - found = False - for (key, path) in url_map: - try: - resp = url_helper.readurl(url=self.metadata_address + path, - timeout=self.timeout, - retries=self.retries) - if resp.code == 200: - found = True - self.metadata[key] = resp.contents - else: - LOG.warn("Path: %s returned %s", path, resp.code) - return False - except url_helper.UrlError as e: - LOG.warn("Path: %s raised exception: %s", path, e) - return False - - return found + caller = functools.partial(util.read_file_or_url, timeout=self.timeout, + retries=self.retries) + md = ec2_utils.MetadataMaterializer(str(caller(self.metadata_address)), + base_url=self.metadata_address, + caller=caller) + + self.metadata = md.materialize() + + if self.metadata.get('id'): + return True + else: + return False def get_userdata_raw(self): - return self.metadata['user-data'] + return "\n".join(self.metadata['user-data']) def get_vendordata_raw(self): - return self.metadata['vendor-data'] + return "\n".join(self.metadata['vendor-data']) def get_public_ssh_keys(self): - return self.metadata['public-keys'].splitlines() + return self.metadata['public-keys'].splitlines() @property def availability_zone(self): diff --git a/tests/unittests/test_datasource/test_digitalocean.py b/tests/unittests/test_datasource/test_digitalocean.py index 9576e042..559a4f9f 100644 --- a/tests/unittests/test_datasource/test_digitalocean.py +++ b/tests/unittests/test_datasource/test_digitalocean.py @@ -26,7 +26,16 @@ from cloudinit.sources import DataSourceDigitalOcean from .. import helpers as test_helpers +# Abbreviated for the test +DO_INDEX = """id + hostname + user-data + vendor-data + public-keys + region""" + DO_META = { + '': DO_INDEX, 'user-data': '#!/bin/bash\necho "user-data"', 'vendor-data': '#!/bin/bash\necho "vendor-data"', 'public-keys': 'ssh-rsa AAAAB3NzaC1yc2EAAAA... neal@digitalocean.com', -- cgit v1.2.3 From 8eedba0d3b9851bb0101407c5a070b7a975efa04 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 17 Oct 2014 12:32:41 -0700 Subject: Expose uses_systemd as a distro function Without this change the tests are currently failing on rhel7 since a location where a hostname file is written no longer exists at that location when systemd is active. To avoid this allow the test to inspect if the distro has systemd enabled and avoid testing the file when systemd is being used so the test passes. We likely need to figure out a better way to test features that no longer exist as files but exist as commands with systemd in general. --- cloudinit/distros/rhel.py | 12 ++++++------ tests/unittests/test_handler/test_handler_set_hostname.py | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index e8abf111..1a269e08 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -98,7 +98,7 @@ class Distro(distros.Distro): rhel_util.update_sysconfig_file(self.network_conf_fn, net_cfg) return dev_names - def _dist_uses_systemd(self): + def uses_systemd(self): # Fedora 18 and RHEL 7 were the first adopters in their series (dist, vers) = util.system_info()['dist'][:2] major = (int)(vers.split('.')[0]) @@ -106,7 +106,7 @@ class Distro(distros.Distro): or (dist.startswith('Fedora') and major >= 18)) def apply_locale(self, locale, out_fn=None): - if self._dist_uses_systemd(): + if self.uses_systemd(): if not out_fn: out_fn = self.systemd_locale_conf_fn out_fn = self.systemd_locale_conf_fn @@ -119,7 +119,7 @@ class Distro(distros.Distro): rhel_util.update_sysconfig_file(out_fn, locale_cfg) def _write_hostname(self, hostname, out_fn): - if self._dist_uses_systemd(): + if self.uses_systemd(): util.subp(['hostnamectl', 'set-hostname', str(hostname)]) else: host_cfg = { @@ -135,14 +135,14 @@ class Distro(distros.Distro): return hostname def _read_system_hostname(self): - if self._dist_uses_systemd(): + if self.uses_systemd(): host_fn = self.systemd_hostname_conf_fn else: host_fn = self.hostname_conf_fn return (host_fn, self._read_hostname(host_fn)) def _read_hostname(self, filename, default=None): - if self._dist_uses_systemd(): + if self.uses_systemd(): (out, _err) = util.subp(['hostname']) if len(out): return out @@ -163,7 +163,7 @@ class Distro(distros.Distro): def set_timezone(self, tz): tz_file = self._find_tz_file(tz) - if self._dist_uses_systemd(): + if self.uses_systemd(): # Currently, timedatectl complains if invoked during startup # so for compatibility, create the link manually. util.del_file(self.tz_local_fn) diff --git a/tests/unittests/test_handler/test_handler_set_hostname.py b/tests/unittests/test_handler/test_handler_set_hostname.py index 03004ab9..e1530e30 100644 --- a/tests/unittests/test_handler/test_handler_set_hostname.py +++ b/tests/unittests/test_handler/test_handler_set_hostname.py @@ -37,10 +37,11 @@ class TestHostname(t_help.FilesystemMockingTestCase): self.patchUtils(self.tmp) cc_set_hostname.handle('cc_set_hostname', cfg, cc, LOG, []) - contents = util.load_file("/etc/sysconfig/network") - n_cfg = ConfigObj(StringIO(contents)) - self.assertEquals({'HOSTNAME': 'blah.blah.blah.yahoo.com'}, - dict(n_cfg)) + if not distro.uses_systemd(): + contents = util.load_file("/etc/sysconfig/network") + n_cfg = ConfigObj(StringIO(contents)) + self.assertEquals({'HOSTNAME': 'blah.blah.blah.yahoo.com'}, + dict(n_cfg)) def test_write_hostname_debian(self): cfg = { -- cgit v1.2.3 From fdef6a9f84c5720bbd37f3eb98c9b7c58913bbfd Mon Sep 17 00:00:00 2001 From: Neal Shrader Date: Fri, 17 Oct 2014 16:26:46 -0400 Subject: Correct handling of single/multiple ssh keys --- cloudinit/sources/DataSourceDigitalOcean.py | 6 ++++- .../unittests/test_datasource/test_digitalocean.py | 30 +++++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/cloudinit/sources/DataSourceDigitalOcean.py b/cloudinit/sources/DataSourceDigitalOcean.py index b7afca93..c59232ca 100644 --- a/cloudinit/sources/DataSourceDigitalOcean.py +++ b/cloudinit/sources/DataSourceDigitalOcean.py @@ -19,6 +19,7 @@ from cloudinit import util from cloudinit import sources from cloudinit import url_helper from cloudinit import ec2_utils +from types import * import functools @@ -71,7 +72,10 @@ class DataSourceDigitalOcean(sources.DataSource): return "\n".join(self.metadata['vendor-data']) def get_public_ssh_keys(self): - return self.metadata['public-keys'].splitlines() + if type(self.metadata['public-keys']) is StringType: + return [self.metadata['public-keys']] + else: + return self.metadata['public-keys'] @property def availability_zone(self): diff --git a/tests/unittests/test_datasource/test_digitalocean.py b/tests/unittests/test_datasource/test_digitalocean.py index 559a4f9f..0997cf38 100644 --- a/tests/unittests/test_datasource/test_digitalocean.py +++ b/tests/unittests/test_datasource/test_digitalocean.py @@ -18,6 +18,7 @@ import httpretty import re +from types import * from urlparse import urlparse from cloudinit import settings @@ -34,11 +35,15 @@ DO_INDEX = """id public-keys region""" +DO_MULTIPLE_KEYS = """ssh-rsa AAAAB3NzaC1yc2EAAAA... neal@digitalocean.com + ssh-rsa AAAAB3NzaC1yc2EAAAA... neal2@digitalocean.com""" +DO_SINGLE_KEY = "ssh-rsa AAAAB3NzaC1yc2EAAAA... neal@digitalocean.com" + DO_META = { '': DO_INDEX, 'user-data': '#!/bin/bash\necho "user-data"', 'vendor-data': '#!/bin/bash\necho "vendor-data"', - 'public-keys': 'ssh-rsa AAAAB3NzaC1yc2EAAAA... neal@digitalocean.com', + 'public-keys': DO_SINGLE_KEY, 'region': 'nyc3', 'id': '2000000', 'hostname': 'cloudinit-test', @@ -88,9 +93,6 @@ class TestDataSourceDigitalOcean(test_helpers.HttprettyTestCase): self.assertEqual(DO_META.get('vendor-data'), self.ds.get_vendordata_raw()) - self.assertEqual([DO_META.get('public-keys')], - self.ds.get_public_ssh_keys()) - self.assertEqual(DO_META.get('region'), self.ds.availability_zone) @@ -102,3 +104,23 @@ class TestDataSourceDigitalOcean(test_helpers.HttprettyTestCase): self.assertEqual('http://mirrors.digitalocean.com/', self.ds.get_package_mirror_info()) + + # Single key + self.assertEqual([DO_META.get('public-keys')], + self.ds.get_public_ssh_keys()) + + self.assertIs(type(self.ds.get_public_ssh_keys()), ListType) + + @httpretty.activate + def test_multiple_ssh_keys(self): + DO_META['public_keys'] = DO_MULTIPLE_KEYS + httpretty.register_uri( + httpretty.GET, MD_URL_RE, + body=_request_callback) + self.ds.get_data() + + # Multiple keys + self.assertEqual(DO_META.get('public-keys').splitlines(), + self.ds.get_public_ssh_keys()) + + self.assertIs(type(self.ds.get_public_ssh_keys()), ListType) -- cgit v1.2.3 From 216d3bf22414ca731a1eac2098e5883d2dab06b1 Mon Sep 17 00:00:00 2001 From: Neal Shrader Date: Fri, 17 Oct 2014 19:00:01 -0400 Subject: Explicitly import only types being compared --- cloudinit/sources/DataSourceDigitalOcean.py | 2 +- tests/unittests/test_datasource/test_digitalocean.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/cloudinit/sources/DataSourceDigitalOcean.py b/cloudinit/sources/DataSourceDigitalOcean.py index c59232ca..b25dcb27 100644 --- a/cloudinit/sources/DataSourceDigitalOcean.py +++ b/cloudinit/sources/DataSourceDigitalOcean.py @@ -19,7 +19,7 @@ from cloudinit import util from cloudinit import sources from cloudinit import url_helper from cloudinit import ec2_utils -from types import * +from types import StringType import functools diff --git a/tests/unittests/test_datasource/test_digitalocean.py b/tests/unittests/test_datasource/test_digitalocean.py index 0997cf38..04bee340 100644 --- a/tests/unittests/test_datasource/test_digitalocean.py +++ b/tests/unittests/test_datasource/test_digitalocean.py @@ -18,7 +18,7 @@ import httpretty import re -from types import * +from types import ListType from urlparse import urlparse from cloudinit import settings -- cgit v1.2.3 From 2d9d3811b4b1d4ea078a0bba6cf5e067339c14f3 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Sat, 18 Oct 2014 09:27:47 -0700 Subject: Pretty up the debug module Previously the usage of the yaml_dumps module was causing any python unicode key and value to show up as: 'item': !!python/unicode "some string" This was not very pretty... Fix this by using safe_dumps (which is also a good thing to use and allow_unicode=True). Also create a tiny helper function in the cc_debug module that does not include the yaml start and end footers (since this module has its own footers and headers). Also includes a basic sanity test for this module. --- cloudinit/config/cc_debug.py | 18 +++-- cloudinit/util.py | 16 ++--- tests/unittests/test_handler/test_handler_debug.py | 78 ++++++++++++++++++++++ 3 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 tests/unittests/test_handler/test_handler_debug.py (limited to 'tests') diff --git a/cloudinit/config/cc_debug.py b/cloudinit/config/cc_debug.py index 7219b0f8..2705035b 100644 --- a/cloudinit/config/cc_debug.py +++ b/cloudinit/config/cc_debug.py @@ -14,11 +14,14 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -from cloudinit import type_utils -from cloudinit import util import copy from StringIO import StringIO +from cloudinit import type_utils +from cloudinit import util + +SKIP_KEYS = frozenset(['log_cfgs']) + def _make_header(text): header = StringIO() @@ -31,6 +34,11 @@ def _make_header(text): return header.getvalue() +def _dumps(obj): + text = util.yaml_dumps(obj, explicit_start=False, explicit_end=False) + return text.rstrip() + + def handle(name, cfg, cloud, log, args): verbose = util.get_cfg_by_path(cfg, ('debug', 'verbose'), default=True) if args: @@ -46,7 +54,7 @@ def handle(name, cfg, cloud, log, args): return # Clean out some keys that we just don't care about showing... dump_cfg = copy.deepcopy(cfg) - for k in ['log_cfgs']: + for k in SKIP_KEYS: dump_cfg.pop(k, None) all_keys = list(dump_cfg.keys()) for k in all_keys: @@ -55,10 +63,10 @@ def handle(name, cfg, cloud, log, args): # Now dump it... to_print = StringIO() to_print.write(_make_header("Config")) - to_print.write(util.yaml_dumps(dump_cfg)) + to_print.write(_dumps(dump_cfg)) to_print.write("\n") to_print.write(_make_header("MetaData")) - to_print.write(util.yaml_dumps(cloud.datasource.metadata)) + to_print.write(_dumps(cloud.datasource.metadata)) to_print.write("\n") to_print.write(_make_header("Misc")) to_print.write("Datasource: %s\n" % diff --git a/cloudinit/util.py b/cloudinit/util.py index f236d0bf..13084374 100644 --- a/cloudinit/util.py +++ b/cloudinit/util.py @@ -1270,14 +1270,14 @@ def read_write_cmdline_url(target_fn): logexc(LOG, "Failed writing url content to %s", target_fn) -def yaml_dumps(obj): - formatted = yaml.dump(obj, - line_break="\n", - indent=4, - explicit_start=True, - explicit_end=True, - default_flow_style=False) - return formatted +def yaml_dumps(obj, explicit_start=True, explicit_end=True): + return yaml.safe_dump(obj, + line_break="\n", + indent=4, + explicit_start=explicit_start, + explicit_end=explicit_end, + default_flow_style=False, + allow_unicode=True) def ensure_dir(path, mode=None): diff --git a/tests/unittests/test_handler/test_handler_debug.py b/tests/unittests/test_handler/test_handler_debug.py new file mode 100644 index 00000000..bd9e29d8 --- /dev/null +++ b/tests/unittests/test_handler/test_handler_debug.py @@ -0,0 +1,78 @@ +# vi: ts=4 expandtab +# +# Copyright (C) 2014 Yahoo! Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 3, as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from cloudinit.config import cc_debug + +from cloudinit import cloud +from cloudinit import distros +from cloudinit import helpers +from cloudinit import util + +from cloudinit.sources import DataSourceNone + +from .. import helpers as t_help + +import logging + +LOG = logging.getLogger(__name__) + + +class TestDebug(t_help.FilesystemMockingTestCase): + def setUp(self): + super(TestDebug, self).setUp() + self.new_root = self.makeDir(prefix="unittest_") + + def _get_cloud(self, distro, metadata=None): + self.patchUtils(self.new_root) + paths = helpers.Paths({}) + cls = distros.fetch(distro) + d = cls(distro, {}, paths) + ds = DataSourceNone.DataSourceNone({}, d, paths) + if metadata: + ds.metadata.update(metadata) + return cloud.Cloud(ds, paths, {}, d, None) + + def test_debug_write(self): + cfg = { + 'abc': '123', + 'c': u'\u20a0', + 'debug': { + 'verbose': True, + # Does not actually write here due to mocking... + 'output': '/var/log/cloud-init-debug.log', + }, + } + cc = self._get_cloud('ubuntu') + cc_debug.handle('cc_debug', cfg, cc, LOG, []) + contents = util.load_file('/var/log/cloud-init-debug.log') + # Some basic sanity tests... + self.assertGreater(len(contents), 0) + for k in cfg.keys(): + self.assertIn(k, contents) + + def test_debug_no_write(self): + cfg = { + 'abc': '123', + 'debug': { + 'verbose': False, + # Does not actually write here due to mocking... + 'output': '/var/log/cloud-init-debug.log', + }, + } + cc = self._get_cloud('ubuntu') + cc_debug.handle('cc_debug', cfg, cc, LOG, []) + self.assertRaises(IOError, + util.load_file, '/var/log/cloud-init-debug.log') -- cgit v1.2.3 From 7a24608072fb47ee9e40d8127b1f0f552bae0818 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Thu, 30 Oct 2014 13:07:41 -0700 Subject: Fix the digital ocean test on py2.6 The digital ocean datasource test is using assertIs which is only created/existent on py2.7, so for the older py2.6 we need to add similar logic so that the test works correctly there. --- tests/unittests/helpers.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tests') diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py index 9700a4ca..52305397 100644 --- a/tests/unittests/helpers.py +++ b/tests/unittests/helpers.py @@ -35,6 +35,11 @@ else: if PY26: # For now add these on, taken from python 2.7 + slightly adjusted class TestCase(unittest.TestCase): + def assertIs(self, expr1, expr2, msg=None): + if expr1 is not expr2: + standardMsg = '%r is not %r' % (expr1, expr2) + self.fail(self._formatMessage(msg, standardMsg)) + def assertIn(self, member, container, msg=None): if member not in container: standardMsg = '%r not found in %r' % (member, container) -- cgit v1.2.3 From bd462bc68506d7da4d7e04b05e947e0cf3f8e19d Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Wed, 12 Nov 2014 13:52:28 +0000 Subject: Add tests for current parse_ssh_config behaviour. This also adds mock as a test dependency, as we are looking to migrate away from mocker. --- test-requirements.txt | 1 + tests/unittests/test_sshutil.py | 58 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/test-requirements.txt b/test-requirements.txt index 2edb8066..230f0404 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1,4 +1,5 @@ httpretty>=0.7.1 +mock mocker nose pep8==1.5.7 diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py index d8662cac..2a496418 100644 --- a/tests/unittests/test_sshutil.py +++ b/tests/unittests/test_sshutil.py @@ -1,6 +1,9 @@ -from cloudinit import ssh_util from unittest import TestCase +from mock import patch + +from cloudinit import ssh_util + VALID_CONTENT = { 'dsa': ( @@ -98,4 +101,57 @@ class TestAuthKeyLineParser(TestCase): self.assertFalse(key.valid()) +class TestParseSSHConfig(TestCase): + + def setUp(self): + self.load_file_patch = patch('cloudinit.ssh_util.util.load_file') + self.load_file = self.load_file_patch.start() + self.isfile_patch = patch('cloudinit.ssh_util.os.path.isfile') + self.isfile = self.isfile_patch.start() + self.isfile.return_value = True + + def tearDown(self): + self.load_file_patch.stop() + self.isfile_patch.stop() + + def test_not_a_file(self): + self.isfile.return_value = False + self.load_file.side_effect = IOError + ret = ssh_util.parse_ssh_config('not a real file') + self.assertEqual([], ret) + + def test_empty_file(self): + self.load_file.return_value = '' + ret = ssh_util.parse_ssh_config('some real file') + self.assertEqual([], ret) + + def test_comment_line(self): + comment_line = '# This is a comment' + self.load_file.return_value = comment_line + ret = ssh_util.parse_ssh_config('some real file') + self.assertEqual(1, len(ret)) + self.assertEqual(comment_line, ret[0].line) + + def test_blank_lines(self): + lines = ['', '\t', ' '] + self.load_file.return_value = '\n'.join(lines) + ret = ssh_util.parse_ssh_config('some real file') + self.assertEqual(len(lines), len(ret)) + for line in ret: + self.assertEqual('', line.line) + + def test_lower_case_config(self): + self.load_file.return_value = 'foo bar' + ret = ssh_util.parse_ssh_config('some real file') + self.assertEqual(1, len(ret)) + self.assertEqual('foo', ret[0].key) + self.assertEqual('bar', ret[0].value) + + def test_upper_case_config(self): + self.load_file.return_value = 'Foo Bar' + ret = ssh_util.parse_ssh_config('some real file') + self.assertEqual(1, len(ret)) + self.assertEqual('foo', ret[0].key) + self.assertEqual('Bar', ret[0].value) + # vi: ts=4 expandtab -- cgit v1.2.3 From 4ecca95e5973707f08fefc43448fec9e0f984966 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Wed, 12 Nov 2014 13:52:28 +0000 Subject: Handle = used as config delimiter in SSH config. --- cloudinit/ssh_util.py | 5 ++++- tests/unittests/test_sshutil.py | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/cloudinit/ssh_util.py b/cloudinit/ssh_util.py index 70a577bc..14d0cb0f 100644 --- a/cloudinit/ssh_util.py +++ b/cloudinit/ssh_util.py @@ -293,7 +293,10 @@ def parse_ssh_config(fname): if not line or line.startswith("#"): lines.append(SshdConfigLine(line)) continue - (key, val) = line.split(None, 1) + try: + key, val = line.split(None, 1) + except ValueError: + key, val = line.split('=', 1) lines.append(SshdConfigLine(line, key, val)) return lines diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py index 2a496418..cd576e8f 100644 --- a/tests/unittests/test_sshutil.py +++ b/tests/unittests/test_sshutil.py @@ -154,4 +154,18 @@ class TestParseSSHConfig(TestCase): self.assertEqual('foo', ret[0].key) self.assertEqual('Bar', ret[0].value) + def test_lower_case_with_equals(self): + self.load_file.return_value = 'foo=bar' + ret = ssh_util.parse_ssh_config('some real file') + self.assertEqual(1, len(ret)) + self.assertEqual('foo', ret[0].key) + self.assertEqual('bar', ret[0].value) + + def test_upper_case_with_equals(self): + self.load_file.return_value = 'Foo=bar' + ret = ssh_util.parse_ssh_config('some real file') + self.assertEqual(1, len(ret)) + self.assertEqual('foo', ret[0].key) + self.assertEqual('bar', ret[0].value) + # vi: ts=4 expandtab -- cgit v1.2.3 From 5d168a9d11e905dd19e24d0596b7d1145e6b8aa1 Mon Sep 17 00:00:00 2001 From: Daniel Watkins Date: Thu, 13 Nov 2014 10:35:46 +0000 Subject: Use test_helpers.TestCase for test_sshutil tests. As requested by harlowja. --- tests/unittests/test_sshutil.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/unittests/test_sshutil.py b/tests/unittests/test_sshutil.py index cd576e8f..3b317121 100644 --- a/tests/unittests/test_sshutil.py +++ b/tests/unittests/test_sshutil.py @@ -1,7 +1,6 @@ -from unittest import TestCase - from mock import patch +from . import helpers as test_helpers from cloudinit import ssh_util @@ -38,7 +37,7 @@ TEST_OPTIONS = ("no-port-forwarding,no-agent-forwarding,no-X11-forwarding," 'user \"root\".\';echo;sleep 10"') -class TestAuthKeyLineParser(TestCase): +class TestAuthKeyLineParser(test_helpers.TestCase): def test_simple_parse(self): # test key line with common 3 fields (keytype, base64, comment) parser = ssh_util.AuthKeyLineParser() @@ -101,7 +100,7 @@ class TestAuthKeyLineParser(TestCase): self.assertFalse(key.valid()) -class TestParseSSHConfig(TestCase): +class TestParseSSHConfig(test_helpers.TestCase): def setUp(self): self.load_file_patch = patch('cloudinit.ssh_util.util.load_file') -- cgit v1.2.3 From 563467f2d62def448dbb8bacf33ae25782b2849e Mon Sep 17 00:00:00 2001 From: Shraddha Pandhe Date: Fri, 21 Nov 2014 22:17:26 +0000 Subject: Added unittests for IPv6 support for RHEL --- cloudinit/distros/rhel.py | 1 + tests/unittests/test_distros/test_netconfig.py | 93 ++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) (limited to 'tests') diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py index fa3ccb38..13335df5 100644 --- a/cloudinit/distros/rhel.py +++ b/cloudinit/distros/rhel.py @@ -106,6 +106,7 @@ class Distro(distros.Distro): # If IPv6 interface present, enable ipv6 networking if use_ipv6: net_cfg['NETWORKING_IPV6'] = _make_sysconfig_bool(True) + net_cfg['IPV6_AUTOCONF'] = _make_sysconfig_bool(False) rhel_util.update_sysconfig_file(self.network_conf_fn, net_cfg) return dev_names diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py index 35cc1f43..cb385da7 100644 --- a/tests/unittests/test_distros/test_netconfig.py +++ b/tests/unittests/test_distros/test_netconfig.py @@ -30,6 +30,24 @@ auto eth1 iface eth1 inet dhcp ''' +BASE_NET_CFG_IPV6 = ''' +auto lo +iface lo inet loopback + +auto eth0 +iface eth0 inet static + address 192.168.1.5 + netmask 255.255.255.0 + network 192.168.0.0 + broadcast 192.168.1.0 + gateway 192.168.1.254 + +iface eth0 inet6 static + address 2607:f0d0:1002:0011::2 + netmask 64 + gateway 2607:f0d0:1002:0011::1 +''' + class WriteBuffer(object): def __init__(self): @@ -174,6 +192,81 @@ NETWORKING=yes self.assertCfgEquals(expected_buf, str(write_buf)) self.assertEquals(write_buf.mode, 0644) + def test_write_ipv6_rhel(self): + rh_distro = self._get_distro('rhel') + write_mock = self.mocker.replace(util.write_file, + spec=False, passthrough=False) + load_mock = self.mocker.replace(util.load_file, + spec=False, passthrough=False) + exists_mock = self.mocker.replace(os.path.isfile, + spec=False, passthrough=False) + + write_bufs = {} + + def replace_write(filename, content, mode=0644, omode="wb"): + buf = WriteBuffer() + buf.mode = mode + buf.omode = omode + buf.write(content) + write_bufs[filename] = buf + + exists_mock(mocker.ARGS) + self.mocker.count(0, None) + self.mocker.result(False) + + load_mock(mocker.ARGS) + self.mocker.count(0, None) + self.mocker.result('') + + for _i in range(0, 2): + write_mock(mocker.ARGS) + self.mocker.call(replace_write) + + write_mock(mocker.ARGS) + self.mocker.call(replace_write) + + self.mocker.replay() + rh_distro.apply_network(BASE_NET_CFG_IPV6, False) + + self.assertEquals(len(write_bufs), 3) + self.assertIn('/etc/sysconfig/network-scripts/ifcfg-lo', write_bufs) + write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-lo'] + expected_buf = ''' +DEVICE="lo" +ONBOOT=yes +''' + self.assertCfgEquals(expected_buf, str(write_buf)) + self.assertEquals(write_buf.mode, 0644) + + self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth0', write_bufs) + write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth0'] + expected_buf = ''' +DEVICE="eth0" +BOOTPROTO="static" +NETMASK="255.255.255.0" +IPADDR="192.168.1.5" +ONBOOT=yes +GATEWAY="192.168.1.254" +BROADCAST="192.168.1.0" +IPV6INIT=yes +IPV6ADDR="2607:f0d0:1002:0011::2" +IPV6_DEFAULTGW="2607:f0d0:1002:0011::1" +''' + self.assertCfgEquals(expected_buf, str(write_buf)) + self.assertEquals(write_buf.mode, 0644) + + self.assertIn('/etc/sysconfig/network', write_bufs) + write_buf = write_bufs['/etc/sysconfig/network'] + expected_buf = ''' +# Created by cloud-init v. 0.7 +NETWORKING=yes +NETWORKING_IPV6=yes +IPV6_AUTOCONF=no +''' + self.assertCfgEquals(expected_buf, str(write_buf)) + self.assertEquals(write_buf.mode, 0644) + + def test_simple_write_freebsd(self): fbsd_distro = self._get_distro('freebsd') util_mock = self.mocker.replace(util.write_file, -- cgit v1.2.3 From a2915dae58bb735a0033b3575db6540b8fee4a4e Mon Sep 17 00:00:00 2001 From: Shraddha Pandhe Date: Mon, 24 Nov 2014 19:54:22 +0000 Subject: Updated unittests + Scenario with multiple NICs --- tests/unittests/test_distros/test_netconfig.py | 32 ++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/unittests/test_distros/test_netconfig.py b/tests/unittests/test_distros/test_netconfig.py index cb385da7..dbbf9617 100644 --- a/tests/unittests/test_distros/test_netconfig.py +++ b/tests/unittests/test_distros/test_netconfig.py @@ -46,6 +46,18 @@ iface eth0 inet6 static address 2607:f0d0:1002:0011::2 netmask 64 gateway 2607:f0d0:1002:0011::1 + +iface eth1 inet static + address 192.168.1.6 + netmask 255.255.255.0 + network 192.168.0.0 + broadcast 192.168.1.0 + gateway 192.168.1.254 + +iface eth1 inet6 static + address 2607:f0d0:1002:0011::3 + netmask 64 + gateway 2607:f0d0:1002:0011::1 ''' @@ -218,7 +230,7 @@ NETWORKING=yes self.mocker.count(0, None) self.mocker.result('') - for _i in range(0, 2): + for _i in range(0, 3): write_mock(mocker.ARGS) self.mocker.call(replace_write) @@ -228,7 +240,7 @@ NETWORKING=yes self.mocker.replay() rh_distro.apply_network(BASE_NET_CFG_IPV6, False) - self.assertEquals(len(write_bufs), 3) + self.assertEquals(len(write_bufs), 4) self.assertIn('/etc/sysconfig/network-scripts/ifcfg-lo', write_bufs) write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-lo'] expected_buf = ''' @@ -251,6 +263,22 @@ BROADCAST="192.168.1.0" IPV6INIT=yes IPV6ADDR="2607:f0d0:1002:0011::2" IPV6_DEFAULTGW="2607:f0d0:1002:0011::1" +''' + self.assertCfgEquals(expected_buf, str(write_buf)) + self.assertEquals(write_buf.mode, 0644) + self.assertIn('/etc/sysconfig/network-scripts/ifcfg-eth1', write_bufs) + write_buf = write_bufs['/etc/sysconfig/network-scripts/ifcfg-eth1'] + expected_buf = ''' +DEVICE="eth1" +BOOTPROTO="static" +NETMASK="255.255.255.0" +IPADDR="192.168.1.6" +ONBOOT=no +GATEWAY="192.168.1.254" +BROADCAST="192.168.1.0" +IPV6INIT=yes +IPV6ADDR="2607:f0d0:1002:0011::3" +IPV6_DEFAULTGW="2607:f0d0:1002:0011::1" ''' self.assertCfgEquals(expected_buf, str(write_buf)) self.assertEquals(write_buf.mode, 0644) -- cgit v1.2.3 From 1db41a6f5283d38dff6de0b0421d51eac869a39c Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 24 Nov 2014 16:41:21 -0800 Subject: Use assertNotEqual which exists on python2.6 Instead of using assertGreater which is new on python2.7 just use assertNotEqual which does exist on python2.6 to perform the same/similar operation. This makes the unittest not break on python2.6 --- tests/unittests/test_handler/test_handler_debug.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/unittests/test_handler/test_handler_debug.py b/tests/unittests/test_handler/test_handler_debug.py index bd9e29d8..8891ca04 100644 --- a/tests/unittests/test_handler/test_handler_debug.py +++ b/tests/unittests/test_handler/test_handler_debug.py @@ -59,7 +59,7 @@ class TestDebug(t_help.FilesystemMockingTestCase): cc_debug.handle('cc_debug', cfg, cc, LOG, []) contents = util.load_file('/var/log/cloud-init-debug.log') # Some basic sanity tests... - self.assertGreater(len(contents), 0) + self.assertNotEqual(0, len(contents)) for k in cfg.keys(): self.assertIn(k, contents) -- cgit v1.2.3