From 635fec38d173a047cad471e20d17b85c30c5a486 Mon Sep 17 00:00:00 2001 From: Katie McLaughlin Date: Thu, 11 Oct 2018 16:31:05 +0000 Subject: Correct spelling in an error message (udevadm). Fix a typo in a RuntimeError path. (udevamd -> udevadm). --- cloudinit/config/cc_disk_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_disk_setup.py b/cloudinit/config/cc_disk_setup.py index 943089e0..29e192e8 100644 --- a/cloudinit/config/cc_disk_setup.py +++ b/cloudinit/config/cc_disk_setup.py @@ -743,7 +743,7 @@ def assert_and_settle_device(device): util.udevadm_settle() if not os.path.exists(device): raise RuntimeError("Device %s did not exist and was not created " - "with a udevamd settle." % device) + "with a udevadm settle." % device) # Whether or not the device existed above, it is possible that udev # events that would populate udev database (for reading by lsdname) have -- cgit v1.2.3 From a3812a4ab4eeb2aa185eb4a2de186cc60ddd7c03 Mon Sep 17 00:00:00 2001 From: Igor Galić Date: Wed, 14 Nov 2018 22:02:18 +0000 Subject: resizefs: Prefix discovered devpath with '/dev/' when path does not exist In some environments, like FreeBSD, gpart can return the device basename instead of the full path. If this discovered devpath does not exist and is missing the '/dev/' prefix, add that prefix in an attempt to find the device. --- cloudinit/config/cc_resizefs.py | 7 ++++ .../test_handler/test_handler_resizefs.py | 48 ++++++++++++++++++---- 2 files changed, 47 insertions(+), 8 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_resizefs.py b/cloudinit/config/cc_resizefs.py index 2edddd0c..076b9d5a 100644 --- a/cloudinit/config/cc_resizefs.py +++ b/cloudinit/config/cc_resizefs.py @@ -197,6 +197,13 @@ def maybe_get_writable_device_path(devpath, info, log): if devpath.startswith('gpt/'): log.debug('We have a gpt label - just go ahead') return devpath + # Alternatively, our device could simply be a name as returned by gpart, + # such as da0p3 + if not devpath.startswith('/dev/') and not os.path.exists(devpath): + fulldevpath = '/dev/' + devpath.lstrip('/') + log.debug("'%s' doesn't appear to be a valid device path. Trying '%s'", + devpath, fulldevpath) + devpath = fulldevpath try: statret = os.stat(devpath) diff --git a/tests/unittests/test_handler/test_handler_resizefs.py b/tests/unittests/test_handler/test_handler_resizefs.py index feca56c2..6ebacb1a 100644 --- a/tests/unittests/test_handler/test_handler_resizefs.py +++ b/tests/unittests/test_handler/test_handler_resizefs.py @@ -173,6 +173,38 @@ class TestResizefs(CiTestCase): self.assertEqual(('zpool', 'online', '-e', 'vmzroot', disk), ret) + @mock.patch('cloudinit.util.is_container', return_value=False) + @mock.patch('cloudinit.util.get_mount_info') + @mock.patch('cloudinit.util.get_device_info_from_zpool') + @mock.patch('cloudinit.util.parse_mount') + def test_handle_modern_zfsroot(self, mount_info, zpool_info, parse_mount, + is_container): + devpth = 'zroot/ROOT/default' + disk = 'da0p3' + fs_type = 'zfs' + mount_point = '/' + + mount_info.return_value = (devpth, fs_type, mount_point) + zpool_info.return_value = disk + parse_mount.return_value = (devpth, fs_type, mount_point) + + cfg = {'resize_rootfs': True} + + def fake_stat(devpath): + if devpath == disk: + raise OSError("not here") + FakeStat = namedtuple( + 'FakeStat', ['st_mode', 'st_size', 'st_mtime']) # minimal stat + return FakeStat(25008, 0, 1) # fake char block device + + with mock.patch('cloudinit.config.cc_resizefs.do_resize') as dresize: + with mock.patch('cloudinit.config.cc_resizefs.os.stat') as m_stat: + m_stat.side_effect = fake_stat + handle('cc_resizefs', cfg, _cloud=None, log=LOG, args=[]) + + self.assertEqual(('zpool', 'online', '-e', 'zroot', '/dev/' + disk), + dresize.call_args[0][0]) + class TestRootDevFromCmdline(CiTestCase): @@ -246,39 +278,39 @@ class TestMaybeGetDevicePathAsWritableBlock(CiTestCase): def test_maybe_get_writable_device_path_does_not_exist(self): """When devpath does not exist, a warning is logged.""" - info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' + info = 'dev=/dev/I/dont/exist mnt_point=/ path=/dev/none' devpath = wrap_and_call( 'cloudinit.config.cc_resizefs.util', {'is_container': {'return_value': False}}, - maybe_get_writable_device_path, '/I/dont/exist', info, LOG) + maybe_get_writable_device_path, '/dev/I/dont/exist', info, LOG) self.assertIsNone(devpath) self.assertIn( - "WARNING: Device '/I/dont/exist' did not exist." + "WARNING: Device '/dev/I/dont/exist' did not exist." ' cannot resize: %s' % info, self.logs.getvalue()) def test_maybe_get_writable_device_path_does_not_exist_in_container(self): """When devpath does not exist in a container, log a debug message.""" - info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' + info = 'dev=/dev/I/dont/exist mnt_point=/ path=/dev/none' devpath = wrap_and_call( 'cloudinit.config.cc_resizefs.util', {'is_container': {'return_value': True}}, - maybe_get_writable_device_path, '/I/dont/exist', info, LOG) + maybe_get_writable_device_path, '/dev/I/dont/exist', info, LOG) self.assertIsNone(devpath) self.assertIn( - "DEBUG: Device '/I/dont/exist' did not exist in container." + "DEBUG: Device '/dev/I/dont/exist' did not exist in container." ' cannot resize: %s' % info, self.logs.getvalue()) def test_maybe_get_writable_device_path_raises_oserror(self): """When unexpected OSError is raises by os.stat it is reraised.""" - info = 'dev=/I/dont/exist mnt_point=/ path=/dev/none' + info = 'dev=/dev/I/dont/exist mnt_point=/ path=/dev/none' with self.assertRaises(OSError) as context_manager: wrap_and_call( 'cloudinit.config.cc_resizefs', {'util.is_container': {'return_value': True}, 'os.stat': {'side_effect': OSError('Something unexpected')}}, - maybe_get_writable_device_path, '/I/dont/exist', info, LOG) + maybe_get_writable_device_path, '/dev/I/dont/exist', info, LOG) self.assertEqual( 'Something unexpected', str(context_manager.exception)) -- cgit v1.2.3 From a4007d063f96b82545aa678ef2cb472ea3b48b1e Mon Sep 17 00:00:00 2001 From: James Baxter Date: Thu, 6 Dec 2018 18:26:32 +0000 Subject: write_files: add support for appending to files. Add 'append: true' to write_files entries to append 'content' to file specified by 'path' key. This modifies the file open mode to append. --- cloudinit/config/cc_write_files.py | 7 ++++++- tests/unittests/test_handler/test_handler_write_files.py | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_write_files.py b/cloudinit/config/cc_write_files.py index 31d1db61..0b6546e2 100644 --- a/cloudinit/config/cc_write_files.py +++ b/cloudinit/config/cc_write_files.py @@ -49,6 +49,10 @@ binary gzip data can be specified and will be decoded before being written. ... path: /bin/arch permissions: '0555' + - content: | + 15 * * * * root ship_logs + path: /etc/crontab + append: true """ import base64 @@ -113,7 +117,8 @@ def write_files(name, files): contents = extract_contents(f_info.get('content', ''), extractions) (u, g) = util.extract_usergroup(f_info.get('owner', DEFAULT_OWNER)) perms = decode_perms(f_info.get('permissions'), DEFAULT_PERMS) - util.write_file(path, contents, mode=perms) + omode = 'ab' if util.get_cfg_option_bool(f_info, 'append') else 'wb' + util.write_file(path, contents, omode=omode, mode=perms) util.chownbyname(path, u, g) diff --git a/tests/unittests/test_handler/test_handler_write_files.py b/tests/unittests/test_handler/test_handler_write_files.py index 7fa8fd21..bc8756ca 100644 --- a/tests/unittests/test_handler/test_handler_write_files.py +++ b/tests/unittests/test_handler/test_handler_write_files.py @@ -52,6 +52,18 @@ class TestWriteFiles(FilesystemMockingTestCase): "test_simple", [{"content": expected, "path": filename}]) self.assertEqual(util.load_file(filename), expected) + def test_append(self): + self.patchUtils(self.tmp) + existing = "hello " + added = "world\n" + expected = existing + added + filename = "/tmp/append.file" + util.write_file(filename, existing) + write_files( + "test_append", + [{"content": added, "path": filename, "append": "true"}]) + self.assertEqual(util.load_file(filename), expected) + def test_yaml_binary(self): self.patchUtils(self.tmp) data = util.load_yaml(YAML_TEXT) -- cgit v1.2.3