diff --git a/0001-Set-syslog_fix_perms-for-SUSE-distro-addresses-bsc-1.patch b/0001-Set-syslog_fix_perms-for-SUSE-distro-addresses-bsc-1.patch deleted file mode 100644 index 52f12c0..0000000 --- a/0001-Set-syslog_fix_perms-for-SUSE-distro-addresses-bsc-1.patch +++ /dev/null @@ -1,28 +0,0 @@ -From 28167e24ae4a0e744103a71061474836dc6a9559 Mon Sep 17 00:00:00 2001 -From: Robert Schweikert -Date: Tue, 13 Feb 2018 14:28:51 -0500 -Subject: [PATCH] - Set syslog_fix_perms for SUSE distro, addresses bsc#1080595 - + Avoid using the default configuration for syslog ownership the neither - "adm" nor "wheel" groups are part of the default config on SUSE distros - ---- - config/cloud.cfg.tmpl | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl -index 32de9c9b..3ab681ee 100644 ---- a/config/cloud.cfg.tmpl -+++ b/config/cloud.cfg.tmpl -@@ -5,6 +5,9 @@ - {% if variant in ["freebsd"] %} - syslog_fix_perms: root:wheel - {% endif %} -+{% if variant in ["suse"] %} -+syslog_fix_perms: root:adm -+{% endif %} - # A set of users which may be applied and/or used by various modules - # when a 'default' entry is found it will reference the 'default_user' - # from the distro configuration specified below --- -2.13.6 - diff --git a/0001-Support-chrony-configuration-lp-1731619.patch b/0001-Support-chrony-configuration-lp-1731619.patch index cfe4a66..fd18096 100644 --- a/0001-Support-chrony-configuration-lp-1731619.patch +++ b/0001-Support-chrony-configuration-lp-1731619.patch @@ -28,10 +28,8 @@ Subject: [PATCH 1/3] - Support chrony configuration (lp#1731619) + Add a 13 files changed, 400 insertions(+), 40 deletions(-) create mode 100644 templates/chrony.conf.tmpl -diff --git a/cloudinit/config/cc_ntp.py b/cloudinit/config/cc_ntp.py -index f50bcb35..2f662a9e 100644 ---- a/cloudinit/config/cc_ntp.py -+++ b/cloudinit/config/cc_ntp.py +--- cloudinit/config/cc_ntp.py.orig ++++ cloudinit/config/cc_ntp.py @@ -20,8 +20,9 @@ from textwrap import dedent LOG = logging.getLogger(__name__) @@ -44,30 +42,41 @@ index f50bcb35..2f662a9e 100644 NR_POOL_SERVERS = 4 distros = ['centos', 'debian', 'fedora', 'opensuse', 'sles', 'ubuntu'] -@@ -110,26 +111,48 @@ def handle(name, cfg, cloud, log, _args): - " but not a dictionary type," - " is a %s %instead"), type_utils.obj_name(ntp_cfg)) +@@ -49,6 +50,7 @@ schema = { + 'examples': [ + dedent("""\ + ntp: ++ enabled: true + pools: [0.int.pool.ntp.org, 1.int.pool.ntp.org, ntp.myorg.org] + servers: + - ntp.server.local +@@ -60,6 +62,9 @@ schema = { + 'ntp': { + 'type': ['object', 'null'], + 'properties': { ++ 'enabled': { ++ "type": "boolean" ++ }, + 'pools': { + 'type': 'array', + 'items': { +@@ -110,26 +115,48 @@ def handle(name, cfg, cloud, log, _args) + "'ntp' key existed in config, but not a dictionary type," + " is a {_type} instead".format(_type=type_utils.obj_name(ntp_cfg))) -- validate_cloudconfig_schema(cfg, schema) ++ if ntp_cfg.get('enabled') and ntp_cfg.get('enabled') == 'true': ++ cloud.distro.set_timesync_client() ++ else: ++ # When all distro implementations are switched return here ++ pass ++ + validate_cloudconfig_schema(cfg, schema) - if ntp_installable(): - service_name = 'ntp' - confpath = NTP_CONF - template_name = None - packages = ['ntp'] - check_exe = 'ntpd' -+ if ntp_cfg.get('enabled') and ntp_cfg.get('enabled') == 'true': -+ cloud.distro.set_timesync_client() - else: -- service_name = 'systemd-timesyncd' -- confpath = TIMESYNCD_CONF -- template_name = 'timesyncd.conf' -- packages = [] -- check_exe = '/lib/systemd/systemd-timesyncd' -+ # When all distro implementations are switched return here -+ pass - -- rename_ntp_conf() -+ validate_cloudconfig_schema(cfg, schema) + if hasattr(cloud.distro, 'timesync_client'): + client_name = cloud.distro.timesync_client + service_name = cloud.distro.timesync_service_name @@ -79,8 +88,13 @@ index f50bcb35..2f662a9e 100644 + template_name = 'timesyncd.conf' + elif client_name == 'chrony': + confpath = CHRONY_CONF_FILE -+ template_name = 'chrony.conf' -+ else: ++ template_name = 'chrony.conf.%s' % cloud.distro.name + else: +- service_name = 'systemd-timesyncd' +- confpath = TIMESYNCD_CONF +- template_name = 'timesyncd.conf' +- packages = [] +- check_exe = '/lib/systemd/systemd-timesyncd' + if ntp_installable(): + service_name = 'ntp' + confpath = NTP_CONF_FILE @@ -93,7 +107,8 @@ index f50bcb35..2f662a9e 100644 + template_name = 'timesyncd.conf' + packages = [] + check_exe = '/lib/systemd/systemd-timesyncd' -+ + +- rename_ntp_conf() + rename_ntp_conf(confpath) # ensure when ntp is installed it has a configuration file # to use instead of starting up with packaged defaults @@ -108,7 +123,7 @@ index f50bcb35..2f662a9e 100644 try: reload_ntp(service_name, systemd=cloud.distro.uses_systemd()) -@@ -167,7 +190,7 @@ def install_ntp(install_func, packages=None, check_exe="ntpd"): +@@ -167,7 +194,7 @@ def install_ntp(install_func, packages=N def rename_ntp_conf(config=None): """Rename any existing ntp.conf file""" if config is None: # For testing @@ -117,11 +132,9 @@ index f50bcb35..2f662a9e 100644 if os.path.exists(config): util.rename(config, config + ".dist") -diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py -index 99e60e7a..41ae097d 100755 ---- a/cloudinit/distros/__init__.py -+++ b/cloudinit/distros/__init__.py -@@ -57,6 +57,9 @@ class Distro(object): +--- cloudinit/distros/__init__.py.orig ++++ cloudinit/distros/__init__.py +@@ -61,6 +61,9 @@ class Distro(object): init_cmd = ['service'] # systemctl, service etc renderer_configs = {} @@ -131,7 +144,7 @@ index 99e60e7a..41ae097d 100755 def __init__(self, name, cfg, paths): self._paths = paths self._cfg = cfg -@@ -86,6 +89,43 @@ class Distro(object): +@@ -90,6 +93,43 @@ class Distro(object): renderer.render_network_config(network_config=network_config) return [] @@ -175,149 +188,9 @@ index 99e60e7a..41ae097d 100755 def _find_tz_file(self, tz): tz_file = os.path.join(self.tz_zone_dir, str(tz)) if not os.path.isfile(tz_file): -diff --git a/cloudinit/distros/arch.py b/cloudinit/distros/arch.py -index f87a3432..fffc1c9c 100644 ---- a/cloudinit/distros/arch.py -+++ b/cloudinit/distros/arch.py -@@ -153,6 +153,10 @@ class Distro(distros.Distro): - self._runner.run("update-sources", self.package_command, - ["-y"], freq=PER_INSTANCE) - -+ def _set_default_timesync_client(self): -+ # Fall back to previous implementation -+ return -+ - - def _render_network(entries, target="/", conf_dir="etc/netctl", - resolv_conf="etc/resolv.conf", enable_func=None): -diff --git a/cloudinit/distros/debian.py b/cloudinit/distros/debian.py -index 33cc0bf1..46dd4173 100644 ---- a/cloudinit/distros/debian.py -+++ b/cloudinit/distros/debian.py -@@ -212,6 +212,10 @@ class Distro(distros.Distro): - (arch, _err) = util.subp(['dpkg', '--print-architecture']) - return str(arch).strip() - -+ def _set_default_timesync_client(self): -+ # Fall back to previous implementation -+ return -+ - - def _get_wrapper_prefix(cmd, mode): - if isinstance(cmd, str): -diff --git a/cloudinit/distros/freebsd.py b/cloudinit/distros/freebsd.py -index bad112fe..00b38917 100644 ---- a/cloudinit/distros/freebsd.py -+++ b/cloudinit/distros/freebsd.py -@@ -649,4 +649,8 @@ class Distro(distros.Distro): - self._runner.run("update-sources", self.package_command, - ["update"], freq=PER_INSTANCE) - -+ def _set_default_timesync_client(self): -+ # Fall back to previous implementation -+ return -+ - # vi: ts=4 expandtab -diff --git a/cloudinit/distros/gentoo.py b/cloudinit/distros/gentoo.py -index dc57717d..5685b058 100644 ---- a/cloudinit/distros/gentoo.py -+++ b/cloudinit/distros/gentoo.py -@@ -214,6 +214,10 @@ class Distro(distros.Distro): - self._runner.run("update-sources", self.package_command, - ["-u", "world"], freq=PER_INSTANCE) - -+ def _set_default_timesync_client(self): -+ # Fall back to previous implementation -+ return -+ - - def convert_resolv_conf(settings): - """Returns a settings string formatted for resolv.conf.""" -diff --git a/cloudinit/distros/opensuse.py b/cloudinit/distros/opensuse.py -index a219e9fb..092d6a11 100644 ---- a/cloudinit/distros/opensuse.py -+++ b/cloudinit/distros/opensuse.py -@@ -8,6 +8,8 @@ - # - # This file is part of cloud-init. See LICENSE file for license information. - -+import platform -+ - from cloudinit import distros - - from cloudinit.distros.parsers.hostname import HostnameConf -@@ -36,6 +38,23 @@ class Distro(distros.Distro): - systemd_locale_conf_fn = '/etc/locale.conf' - tz_local_fn = '/etc/localtime' - -+ __timesync_client_map = { -+ # Map the system_info supported values -+ 'chrony': ('chrony', 'chronyd'), -+ 'isc-ntp': ('ntp', 'ntpd'), -+ 'systemd-timesyncd': ('systemd-timesyncd', 'systemd-timesyncd'), -+ # Map the common names if different from system_info -+ 'chronyd': ('chrony', 'chronyd'), -+ 'ntpd': ('ntp', 'ntpd'), -+ '/usr/lib/systemd/systemd-timesyncd': -+ ('systemd-timesyncd', 'systemd-timesyncd') -+ } -+ __ntp_client_execs = [ -+ 'chronyd', -+ 'ntpd', -+ '/usr/lib/systemd/systemd-timesyncd' -+ ] -+ - def __init__(self, name, cfg, paths): - distros.Distro.__init__(self, name, cfg, paths) - self._runner = helpers.Runners(paths) -@@ -145,6 +164,28 @@ class Distro(distros.Distro): - host_fn = self.hostname_conf_fn - return (host_fn, self._read_hostname(host_fn)) - -+ def _set_default_timesync_client(self): -+ """The default timesync client is dependent on the distribution.""" -+ # When we get here the user has configured ntp to be enabled but -+ # no client is installed -+ distro_info = platform.linux_distribution() -+ name = distro_info[0] -+ major_ver = int(distro_info[1].split('.')[0]) -+ -+ # This is horribly complicated because of a case of -+ # "we do not care if versions should be increasing syndrome" -+ if ( -+ (major_ver >= 15 and 'openSUSE' not in name) or -+ (major_ver >= 15 and 'openSUSE' in name and major_ver != 42) -+ ): -+ self.timesync_client = 'chrony' -+ self.timesync_service_name = 'chronyd' -+ self.install_packages(['chrony']) -+ else: -+ self.timesync_client = 'ntp' -+ self.timesync_service_name = 'ntpd' -+ self.install_packages(['ntp']) -+ - def _write_hostname(self, hostname, out_fn): - if self.uses_systemd() and out_fn.endswith('/previous-hostname'): - util.write_file(out_fn, hostname) -diff --git a/cloudinit/distros/rhel.py b/cloudinit/distros/rhel.py -index 1fecb619..6d9c9f67 100644 ---- a/cloudinit/distros/rhel.py -+++ b/cloudinit/distros/rhel.py -@@ -218,4 +218,8 @@ class Distro(distros.Distro): - self._runner.run("update-sources", self.package_command, - ["makecache"], freq=PER_INSTANCE) - -+ def _set_default_timesync_client(self): -+ # Fall back to previous implementation -+ return -+ - # vi: ts=4 expandtab -diff --git a/templates/chrony.conf.tmpl b/templates/chrony.conf.tmpl -new file mode 100644 -index 00000000..38e84d85 --- /dev/null -+++ b/templates/chrony.conf.tmpl -@@ -0,0 +1,25 @@ ++++ templates/chrony.conf.tmpl +@@ -0,0 +1,24 @@ +## template:jinja +# cloud-init generated file +# See chrony.conf(5) @@ -342,441 +215,58 @@ index 00000000..38e84d85 + +# Enable kernel synchronization of the real-time clock (RTC). +rtcsync -+ -diff --git a/tests/unittests/test_distros/test_generic.py b/tests/unittests/test_distros/test_generic.py -index 791fe612..cdee4b1b 100644 ---- a/tests/unittests/test_distros/test_generic.py -+++ b/tests/unittests/test_distros/test_generic.py -@@ -4,16 +4,12 @@ from cloudinit import distros - from cloudinit import util +--- cloudinit/distros/opensuse.py.orig ++++ cloudinit/distros/opensuse.py +@@ -36,6 +36,23 @@ class Distro(distros.Distro): + systemd_locale_conf_fn = '/etc/locale.conf' + tz_local_fn = '/etc/localtime' - from cloudinit.tests import helpers -+from cloudinit.tests.helpers import mock - - import os - import shutil - import tempfile - --try: -- from unittest import mock --except ImportError: -- import mock -- - unknown_arch_info = { - 'arches': ['default'], - 'failsafe': {'primary': 'http://fs-primary-default', -@@ -35,6 +31,24 @@ package_mirrors = [ - unknown_arch_info - ] - -+timesync_user_cfg_chrony = { -+ 'system_info': { -+ 'ntp_client': 'chrony' ++ __timesync_client_map = { ++ # Map the system_info supported values ++ 'chrony': ('chrony', 'chronyd'), ++ 'isc-ntp': ('ntp', 'ntpd'), ++ 'systemd-timesyncd': ('systemd-timesyncd', 'systemd-timesyncd'), ++ # Map the common names if different from system_info ++ 'chronyd': ('chrony', 'chronyd'), ++ 'ntpd': ('ntp', 'ntpd'), ++ '/usr/lib/systemd/systemd-timesyncd': ++ ('systemd-timesyncd', 'systemd-timesyncd') + } -+} ++ __ntp_client_execs = [ ++ 'chronyd', ++ 'ntpd', ++ '/usr/lib/systemd/systemd-timesyncd' ++ ] + -+timesync_user_cfg_ntp = { -+ 'system_info': { -+ 'ntp_client': 'isc-ntp' -+ } -+} -+ -+timesync_user_cfg_systemd = { -+ 'system_info': { -+ 'ntp_client': 'systemd-timesyncd' -+ } -+} -+ - gpmi = distros._get_package_mirror_info - gapmi = distros._get_arch_package_mirror_info + def __init__(self, name, cfg, paths): + distros.Distro.__init__(self, name, cfg, paths) + self._runner = helpers.Runners(paths) +@@ -145,6 +162,28 @@ class Distro(distros.Distro): + host_fn = self.hostname_conf_fn + return (host_fn, self._read_hostname(host_fn)) -@@ -244,5 +258,82 @@ class TestGenericDistro(helpers.FilesystemMockingTestCase): - with self.assertRaises(NotImplementedError): - d.get_locale() - -+ def test_set_timesync_client_user_config_chrony_sles(self): -+ """Test sles distro sets proper values for chrony""" -+ cls = distros.fetch("sles") -+ d = cls("sles", timesync_user_cfg_chrony, None) -+ d.set_timesync_client() -+ self.assertEqual(d.timesync_client, 'chrony') -+ self.assertEqual(d.timesync_service_name, 'chronyd') ++ def _set_default_timesync_client(self): ++ """The default timesync client is dependent on the distribution.""" ++ # When we get here the user has configured ntp to be enabled but ++ # no client is installed ++ distro_info = util.get_linux_distro() ++ name = distro_info[0] ++ major_ver = int(distro_info[1].split('.')[0]) + -+ def test_set_timesync_client_user_config_ntp_sles(self): -+ """Test sles distro sets proper values for ntp""" -+ cls = distros.fetch("sles") -+ d = cls("sles", timesync_user_cfg_ntp, None) -+ d.set_timesync_client() -+ self.assertEqual(d.timesync_client, 'ntp') -+ self.assertEqual(d.timesync_service_name, 'ntpd') -+ -+ def test_set_timesync_client_user_config_timesyncd_sles(self): -+ """Test sles distro sets proper values for timesyncd""" -+ cls = distros.fetch("sles") -+ d = cls("sles", timesync_user_cfg_systemd, None) -+ d.set_timesync_client() -+ self.assertEqual(d.timesync_client, 'systemd-timesyncd') -+ self.assertEqual(d.timesync_service_name, 'systemd-timesyncd') -+ -+ @mock.patch("cloudinit.distros.util") -+ def test_set_timesync_client_chrony_installed_sles(self, mock_util): -+ """Test sles distro sets proper values for chrony if chrony is -+ installed""" -+ mock_util.which.side_effect = side_effect_client_is_chrony -+ cls = distros.fetch("sles") -+ d = cls("sles", {}, None) -+ d.set_timesync_client() -+ self.assertEqual(d.timesync_client, 'chrony') -+ self.assertEqual(d.timesync_service_name, 'chronyd') -+ -+ @mock.patch("cloudinit.distros.util") -+ def test_set_timesync_client_ntp_installed_sles(self, mock_util): -+ """Test sles distro sets proper values for ntp if ntpd is -+ installed""" -+ mock_util.which.side_effect = side_effect_client_is_ntp -+ cls = distros.fetch("sles") -+ d = cls("sles", {}, None) -+ d.set_timesync_client() -+ self.assertEqual(d.timesync_client, 'ntp') -+ self.assertEqual(d.timesync_service_name, 'ntpd') -+ -+ @mock.patch("cloudinit.distros.util") -+ def test_set_timesync_client_timesycd_installed_sles(self, mock_util): -+ """Test sles distro sets proper values for timesycd if timesyncd is -+ installed""" -+ mock_util.which.side_effect = side_effect_client_is_timesyncd -+ cls = distros.fetch("sles") -+ d = cls("sles", {}, None) -+ d.set_timesync_client() -+ self.assertEqual(d.timesync_client, 'systemd-timesyncd') -+ self.assertEqual(d.timesync_service_name, 'systemd-timesyncd') -+ -+ -+def side_effect_client_is_chrony(ntp_client): -+ if 'chrony' in ntp_client: -+ return '/usr/sbin/chronyd' -+ else: -+ return False -+ -+ -+def side_effect_client_is_ntp(ntp_client): -+ if 'ntp' in ntp_client: -+ return '/usr/sbin/ntpd' -+ else: -+ return False -+ -+ -+def side_effect_client_is_timesyncd(ntp_client): -+ if 'timesyncd' in ntp_client: -+ return ntp_client -+ else: -+ return False - - # vi: ts=4 expandtab -diff --git a/tests/unittests/test_distros/test_opensuse.py b/tests/unittests/test_distros/test_opensuse.py -index b9bb9b3e..9ed10af8 100644 ---- a/tests/unittests/test_distros/test_opensuse.py -+++ b/tests/unittests/test_distros/test_opensuse.py -@@ -1,6 +1,6 @@ - # This file is part of cloud-init. See LICENSE file for license information. - --from cloudinit.tests.helpers import CiTestCase -+from cloudinit.tests.helpers import CiTestCase, mock - - from . import _get_distro - -@@ -10,3 +10,45 @@ class TestopenSUSE(CiTestCase): - def test_get_distro(self): - distro = _get_distro("opensuse") - self.assertEqual(distro.osfamily, 'suse') -+ -+ @mock.patch("cloudinit.distros.opensuse.Distro.install_packages") -+ @mock.patch("platform.linux_distribution") -+ def test_set_default_timesync_client_osl42( -+ self, -+ mock_distro, -+ mock_install -+ ): -+ mock_distro.return_value = ('openSUSE ', '42.3', 'x86_64') -+ mock_install.return_value = True -+ distro = _get_distro("opensuse") -+ distro._set_default_timesync_client() -+ self.assertEqual(distro.timesync_client, 'ntp') -+ self.assertEqual(distro.timesync_service_name, 'ntpd') -+ -+ @mock.patch("cloudinit.distros.opensuse.Distro.install_packages") -+ @mock.patch("platform.linux_distribution") -+ def test_set_default_timesync_client_os13( -+ self, -+ mock_distro, -+ mock_install -+ ): -+ mock_distro.return_value = ('openSUSE ', '13.1', 'x86_64') -+ mock_install.return_value = True -+ distro = _get_distro("opensuse") -+ distro._set_default_timesync_client() -+ self.assertEqual(distro.timesync_client, 'ntp') -+ self.assertEqual(distro.timesync_service_name, 'ntpd') -+ -+ @mock.patch("cloudinit.distros.opensuse.Distro.install_packages") -+ @mock.patch("platform.linux_distribution") -+ def test_set_default_timesync_client_osl15( -+ self, -+ mock_distro, -+ mock_install -+ ): -+ mock_distro.return_value = ('openSUSE ', '15.1', 'x86_64') -+ mock_install.return_value = True -+ distro = _get_distro("opensuse") -+ distro._set_default_timesync_client() -+ self.assertEqual(distro.timesync_client, 'chrony') -+ self.assertEqual(distro.timesync_service_name, 'chronyd') -diff --git a/tests/unittests/test_distros/test_sles.py b/tests/unittests/test_distros/test_sles.py -index 33e3c457..13237a27 100644 ---- a/tests/unittests/test_distros/test_sles.py -+++ b/tests/unittests/test_distros/test_sles.py -@@ -1,6 +1,6 @@ - # This file is part of cloud-init. See LICENSE file for license information. - --from cloudinit.tests.helpers import CiTestCase -+from cloudinit.tests.helpers import CiTestCase, mock - - from . import _get_distro - -@@ -10,3 +10,31 @@ class TestSLES(CiTestCase): - def test_get_distro(self): - distro = _get_distro("sles") - self.assertEqual(distro.osfamily, 'suse') -+ -+ @mock.patch("cloudinit.distros.opensuse.Distro.install_packages") -+ @mock.patch("platform.linux_distribution") -+ def test_set_default_timesync_client_osl42( -+ self, -+ mock_distro, -+ mock_install -+ ): -+ mock_distro.return_value = ('SLES ', '12.3', 'x86_64') -+ mock_install.return_value = True -+ distro = _get_distro("sles") -+ distro._set_default_timesync_client() -+ self.assertEqual(distro.timesync_client, 'ntp') -+ self.assertEqual(distro.timesync_service_name, 'ntpd') -+ -+ @mock.patch("cloudinit.distros.opensuse.Distro.install_packages") -+ @mock.patch("platform.linux_distribution") -+ def test_set_default_timesync_client_os13( -+ self, -+ mock_distro, -+ mock_install -+ ): -+ mock_distro.return_value = ('SLES ', '15', 'x86_64') -+ mock_install.return_value = True -+ distro = _get_distro("sles") -+ distro._set_default_timesync_client() -+ self.assertEqual(distro.timesync_client, 'chrony') -+ self.assertEqual(distro.timesync_service_name, 'chronyd') -diff --git a/tests/unittests/test_handler/test_handler_ntp.py b/tests/unittests/test_handler/test_handler_ntp.py -index 28a8455d..33fab8c8 100644 ---- a/tests/unittests/test_handler/test_handler_ntp.py -+++ b/tests/unittests/test_handler/test_handler_ntp.py -@@ -10,6 +10,20 @@ import os - from os.path import dirname - import shutil - -+CHRONY_TEMPLATE = b"""\ -+## template: jinja -+{% if pools %}# pools -+{% endif %} -+{% for pool in pools -%} -+pool {{pool}} iburst -+{% endfor %} -+{%- if servers %}# servers -+{% endif %} -+{% for server in servers -%} -+server {{server}} iburst -+{% endfor %} -+""" -+ - NTP_TEMPLATE = b"""\ - ## template: jinja - servers {{servers}} -@@ -79,7 +93,7 @@ class TestNtp(FilesystemMockingTestCase): - """When NTP_CONF exists, rename_ntp moves it.""" - ntpconf = self.tmp_path("ntp.conf", self.new_root) - util.write_file(ntpconf, "") -- with mock.patch("cloudinit.config.cc_ntp.NTP_CONF", ntpconf): -+ with mock.patch("cloudinit.config.cc_ntp.NTP_CONF_FILE", ntpconf): - cc_ntp.rename_ntp_conf() - self.assertFalse(os.path.exists(ntpconf)) - self.assertTrue(os.path.exists("{0}.dist".format(ntpconf))) -@@ -112,7 +126,7 @@ class TestNtp(FilesystemMockingTestCase): - """When NTP_CONF doesn't exist rename_ntp doesn't create a file.""" - ntpconf = self.tmp_path("ntp.conf", self.new_root) - self.assertFalse(os.path.exists(ntpconf)) -- with mock.patch("cloudinit.config.cc_ntp.NTP_CONF", ntpconf): -+ with mock.patch("cloudinit.config.cc_ntp.NTP_CONF_FILE", ntpconf): - cc_ntp.rename_ntp_conf() - self.assertFalse(os.path.exists("{0}.dist".format(ntpconf))) - self.assertFalse(os.path.exists(ntpconf)) -@@ -133,7 +147,7 @@ class TestNtp(FilesystemMockingTestCase): - # Create ntp.conf.tmpl - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.write_ntp_config_template(cfg, mycloud, ntp_conf) - content = util.read_file_or_url('file://' + ntp_conf).contents - self.assertEqual( -@@ -159,7 +173,7 @@ class TestNtp(FilesystemMockingTestCase): - # Create ntp.conf.tmpl. - with open('{0}.{1}.tmpl'.format(ntp_conf, distro), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.write_ntp_config_template(cfg, mycloud, ntp_conf) - content = util.read_file_or_url('file://' + ntp_conf).contents - self.assertEqual( -@@ -178,7 +192,7 @@ class TestNtp(FilesystemMockingTestCase): - # Create ntp.conf.tmpl - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.write_ntp_config_template({}, mycloud, ntp_conf) - content = util.read_file_or_url('file://' + ntp_conf).contents - default_pools = [ -@@ -210,7 +224,7 @@ class TestNtp(FilesystemMockingTestCase): - # Create ntp.conf.tmpl - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - with mock.patch.object(util, 'which', return_value=None): - cc_ntp.handle('notimportant', cfg, mycloud, None, None) - -@@ -239,7 +253,10 @@ class TestNtp(FilesystemMockingTestCase): - with open(template, 'wb') as stream: - stream.write(TIMESYNCD_TEMPLATE) - -- with mock.patch('cloudinit.config.cc_ntp.TIMESYNCD_CONF', tsyncd_conf): -+ with mock.patch( -+ 'cloudinit.config.cc_ntp.TIMESYNCD_CONF_FILE', -+ tsyncd_conf ++ # This is horribly complicated because of a case of ++ # "we do not care if versions should be increasing syndrome" ++ if ( ++ (major_ver >= 15 and 'openSUSE' not in name) or ++ (major_ver >= 15 and 'openSUSE' in name and major_ver != 42) + ): - cc_ntp.handle('notimportant', cfg, mycloud, None, None) - - content = util.read_file_or_url('file://' + tsyncd_conf).contents -@@ -267,7 +284,7 @@ class TestNtp(FilesystemMockingTestCase): - shutil.copy( - tmpl_file, - os.path.join(self.new_root, 'ntp.conf.%s.tmpl' % distro)) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - with mock.patch.object(util, 'which', return_value=[True]): - cc_ntp.handle('notimportant', cfg, mycloud, None, None) - -@@ -300,7 +317,7 @@ class TestNtp(FilesystemMockingTestCase): - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) - for valid_empty_config in valid_empty_configs: -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.handle('cc_ntp', valid_empty_config, cc, None, []) - with open(ntp_conf) as stream: - content = stream.read() -@@ -323,7 +340,7 @@ class TestNtp(FilesystemMockingTestCase): - ntp_conf = os.path.join(self.new_root, 'ntp.conf') - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.handle('cc_ntp', invalid_config, cc, None, []) - self.assertIn( - "Invalid config:\nntp.pools.0: 123 is not of type 'string'\n" -@@ -344,7 +361,7 @@ class TestNtp(FilesystemMockingTestCase): - ntp_conf = os.path.join(self.new_root, 'ntp.conf') - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.handle('cc_ntp', invalid_config, cc, None, []) - self.assertIn( - "Invalid config:\nntp.pools: 123 is not of type 'array'\n" -@@ -366,7 +383,7 @@ class TestNtp(FilesystemMockingTestCase): - ntp_conf = os.path.join(self.new_root, 'ntp.conf') - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.handle('cc_ntp', invalid_config, cc, None, []) - self.assertIn( - "Invalid config:\nntp: Additional properties are not allowed " -@@ -391,7 +408,7 @@ class TestNtp(FilesystemMockingTestCase): - ntp_conf = os.path.join(self.new_root, 'ntp.conf') - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.handle('cc_ntp', invalid_config, cc, None, []) - self.assertIn( - "Invalid config:\nntp.pools: ['0.mypool.org', '0.mypool.org'] has " -@@ -421,7 +438,10 @@ class TestNtp(FilesystemMockingTestCase): - print(template) - with open(template, 'wb') as stream: - stream.write(TIMESYNCD_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.TIMESYNCD_CONF', tsyncd_conf): -+ with mock.patch( -+ 'cloudinit.config.cc_ntp.TIMESYNCD_CONF_FILE', -+ tsyncd_conf -+ ): - cc_ntp.write_ntp_config_template(cfg, mycloud, tsyncd_conf, - template='timesyncd.conf') - -@@ -442,7 +462,7 @@ class TestNtp(FilesystemMockingTestCase): - # Create ntp.conf.tmpl - with open('{0}.tmpl'.format(ntp_conf), 'wb') as stream: - stream.write(NTP_TEMPLATE) -- with mock.patch('cloudinit.config.cc_ntp.NTP_CONF', ntp_conf): -+ with mock.patch('cloudinit.config.cc_ntp.NTP_CONF_FILE', ntp_conf): - cc_ntp.write_ntp_config_template({}, mycloud, ntp_conf) - content = util.read_file_or_url('file://' + ntp_conf).contents - default_pools = [ -@@ -456,5 +476,35 @@ class TestNtp(FilesystemMockingTestCase): - ",".join(default_pools)), - self.logs.getvalue()) - -+ def test_ntp_handler_chrony(self): -+ """Test ntp handler configures chrony""" -+ distro = 'opensuse' -+ cfg = { -+ 'servers': ['192.168.2.1', '192.168.2.2'], -+ 'pools': ['0.mypool.org'], -+ } -+ mycloud = self._get_cloud(distro) -+ mycloud.timesync_client = 'chrony' -+ mycloud.timesync_service_name = 'chronyd' -+ chrony_conf = self.tmp_path("chrony.conf", self.new_root) -+ # Create chrony.conf.tmpl -+ template = '{0}.tmpl'.format(chrony_conf) -+ print(template) -+ with open(template, 'wb') as stream: -+ stream.write(CHRONY_TEMPLATE) -+ with mock.patch( -+ 'cloudinit.config.cc_ntp.CHRONY_CONF_FILE', -+ chrony_conf -+ ): -+ cc_ntp.write_ntp_config_template(cfg, mycloud, chrony_conf, -+ template='chrony.conf') ++ self.timesync_client = 'chrony' ++ self.timesync_service_name = 'chronyd' ++ self.install_packages(['chrony']) ++ else: ++ self.timesync_client = 'ntp' ++ self.timesync_service_name = 'ntpd' ++ self.install_packages(['ntp']) + -+ content = util.read_file_or_url('file://' + chrony_conf).contents -+ expected = '# pools\n' -+ expected += 'pool 0.mypool.org iburst\n' -+ expected += '# servers\n' -+ expected += 'server 192.168.2.1 iburst\n' -+ expected += 'server 192.168.2.2 iburst\n\n' -+ self.assertEqual(expected, content.decode()) - - # vi: ts=4 expandtab --- -2.13.6 - + def _write_hostname(self, hostname, out_fn): + if self.uses_systemd() and out_fn.endswith('/previous-hostname'): + util.write_file(out_fn, hostname) diff --git a/0002-Disable-method-deprecation-warning-for-pylint.patch b/0002-Disable-method-deprecation-warning-for-pylint.patch deleted file mode 100644 index e6c849a..0000000 --- a/0002-Disable-method-deprecation-warning-for-pylint.patch +++ /dev/null @@ -1,27 +0,0 @@ -From d94392bb6e54a6860c8b6ea7967e853d8e263d7a Mon Sep 17 00:00:00 2001 -From: Robert Schweikert -Date: Fri, 8 Dec 2017 17:03:01 -0500 -Subject: [PATCH 2/3] - Disable method deprecation warning for pylint - ---- - cloudinit/distros/opensuse.py | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/cloudinit/distros/opensuse.py b/cloudinit/distros/opensuse.py -index 092d6a11..86318eae 100644 ---- a/cloudinit/distros/opensuse.py -+++ b/cloudinit/distros/opensuse.py -@@ -8,6 +8,10 @@ - # - # This file is part of cloud-init. See LICENSE file for license information. - -+# pylint: disable=W1505 -+# platform.linux_distribution is deprecated (W1505) we need to decide if -+# cloud-init will implement it's own or add a new dependency on the -+# distro module - import platform - - from cloudinit import distros --- -2.13.6 - diff --git a/0003-Distro-dependent-chrony-config-file.patch b/0003-Distro-dependent-chrony-config-file.patch index bacada8..bf1b7b2 100644 --- a/0003-Distro-dependent-chrony-config-file.patch +++ b/0003-Distro-dependent-chrony-config-file.patch @@ -1,67 +1,6 @@ -From 42cb1841035befa5b5823b3321c8fe92f2cb9087 Mon Sep 17 00:00:00 2001 -From: Robert Schweikert -Date: Mon, 18 Dec 2017 14:54:10 -0500 -Subject: [PATCH 3/3] - Distro dependent chrony config file + We all like to - stor ethe drift file in different places and name it differently :( - ---- - cloudinit/config/cc_ntp.py | 8 +++++-- - ...{chrony.conf.tmpl => chrony.conf.opensuse.tmpl} | 0 - templates/chrony.conf.sles.tmpl | 25 ++++++++++++++++++++++ - 3 files changed, 31 insertions(+), 2 deletions(-) - rename templates/{chrony.conf.tmpl => chrony.conf.opensuse.tmpl} (100%) - create mode 100644 templates/chrony.conf.sles.tmpl - -diff --git a/cloudinit/config/cc_ntp.py b/cloudinit/config/cc_ntp.py -index 2f662a9e..1db648bc 100644 ---- a/cloudinit/config/cc_ntp.py -+++ b/cloudinit/config/cc_ntp.py -@@ -50,6 +50,7 @@ schema = { - 'examples': [ - dedent("""\ - ntp: -+ enabled: true - pools: [0.int.pool.ntp.org, 1.int.pool.ntp.org, ntp.myorg.org] - servers: - - ntp.server.local -@@ -61,6 +62,9 @@ schema = { - 'ntp': { - 'type': ['object', 'null'], - 'properties': { -+ 'enabled': { -+ "type": "boolean" -+ }, - 'pools': { - 'type': 'array', - 'items': { -@@ -109,7 +113,7 @@ def handle(name, cfg, cloud, log, _args): - if not isinstance(ntp_cfg, (dict)): - raise RuntimeError(("'ntp' key existed in config," - " but not a dictionary type," -- " is a %s %instead"), type_utils.obj_name(ntp_cfg)) -+ " is a %s instead"), type_utils.obj_name(ntp_cfg)) - - if ntp_cfg.get('enabled') and ntp_cfg.get('enabled') == 'true': - cloud.distro.set_timesync_client() -@@ -129,7 +133,7 @@ def handle(name, cfg, cloud, log, _args): - template_name = 'timesyncd.conf' - elif client_name == 'chrony': - confpath = CHRONY_CONF_FILE -- template_name = 'chrony.conf' -+ template_name = 'chrony.conf.%s' % cloud.distro.name - else: - if ntp_installable(): - service_name = 'ntp' -diff --git a/templates/chrony.conf.tmpl b/templates/chrony.conf.opensuse.tmpl -similarity index 100% -rename from templates/chrony.conf.tmpl -rename to templates/chrony.conf.opensuse.tmpl -diff --git a/templates/chrony.conf.sles.tmpl b/templates/chrony.conf.sles.tmpl -new file mode 100644 -index 00000000..38e84d85 --- /dev/null -+++ b/templates/chrony.conf.sles.tmpl -@@ -0,0 +1,25 @@ ++++ templates/chrony.conf.sles.tmpl +@@ -0,0 +1,24 @@ +## template:jinja +# cloud-init generated file +# See chrony.conf(5) @@ -86,7 +25,30 @@ index 00000000..38e84d85 + +# Enable kernel synchronization of the real-time clock (RTC). +rtcsync +--- /dev/null ++++ templates/chrony.conf.opensuse.tmpl +@@ -0,0 +1,24 @@ ++## template:jinja ++# cloud-init generated file ++# See chrony.conf(5) + --- -2.13.6 - ++{% if pools %}# pools ++{% endif %} ++{% for pool in pools -%} ++pool {{pool}} iburst ++{% endfor %} ++{%- if servers %}# servers ++{% endif %} ++{% for server in servers -%} ++server {{server}} iburst ++{% endfor %} ++ ++# Record the rate at which the the system clock gains/losses time ++driftfile /var/lib/chrony/drift ++ ++# Allow the system clock to be stepped in the first three updates ++# if its offset is larger than 1 second. ++makestep 1.0 3 ++ ++# Enable kernel synchronization of the real-time clock (RTC). ++rtcsync diff --git a/cloud-init-17.2.tar.gz b/cloud-init-17.2.tar.gz deleted file mode 100644 index d017e52..0000000 --- a/cloud-init-17.2.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f00338767a8877c8d72fe64a78d3897da822b3c1f47779ae31adc4815275594f -size 810821 diff --git a/cloud-init-18.1.tar.gz b/cloud-init-18.1.tar.gz new file mode 100644 index 0000000..6a42fbf --- /dev/null +++ b/cloud-init-18.1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49d8b7b56adbc6b8bc2aac966954c6c192382ea0500497577f8867e7e25e1ae6 +size 833394 diff --git a/cloud-init-no-python-linux-dist.patch b/cloud-init-no-python-linux-dist.patch index d7efc1a..c0fb755 100644 --- a/cloud-init-no-python-linux-dist.patch +++ b/cloud-init-no-python-linux-dist.patch @@ -1,25 +1,22 @@ ---- /dev/null +--- cloudinit/tests/test_util.py.orig +++ cloudinit/tests/test_util.py -@@ -0,0 +1,129 @@ -+# This file is part of cloud-init. See LICENSE file for license information. -+ -+"""Tests for cloudinit.util""" -+ -+import logging +@@ -3,10 +3,12 @@ + """Tests for cloudinit.util""" + + import logging +import platform -+ -+import cloudinit.util as util -+ -+from cloudinit.tests.helpers import CiTestCase, mock + + import cloudinit.util as util + + from cloudinit.tests.helpers import CiTestCase, mock +from textwrap import dedent -+ -+LOG = logging.getLogger(__name__) -+ -+MOUNT_INFO = [ -+ '68 0 8:3 / / ro,relatime shared:1 - btrfs /dev/sda1 ro,attr2,inode64', -+ '153 68 254:0 / /home rw,relatime shared:101 - xfs /dev/sda2 rw,attr2' -+] -+ + + LOG = logging.getLogger(__name__) + +@@ -15,6 +17,29 @@ MOUNT_INFO = [ + '153 68 254:0 / /home rw,relatime shared:101 - xfs /dev/sda2 rw,attr2' + ] + +OS_RELEASE_SLES = dedent("""\ +NAME="SLES"\n +VERSION="12-SP3"\n @@ -43,35 +40,13 @@ +UBUNTU_CODENAME=xenial\n +""") + -+ -+class TestUtil(CiTestCase): -+ -+ def test_parse_mount_info_no_opts_no_arg(self): -+ result = util.parse_mount_info('/home', MOUNT_INFO, LOG) -+ self.assertEqual(('/dev/sda2', 'xfs', '/home'), result) -+ -+ def test_parse_mount_info_no_opts_arg(self): -+ result = util.parse_mount_info('/home', MOUNT_INFO, LOG, False) -+ self.assertEqual(('/dev/sda2', 'xfs', '/home'), result) -+ -+ def test_parse_mount_info_with_opts(self): -+ result = util.parse_mount_info('/', MOUNT_INFO, LOG, True) -+ self.assertEqual( -+ ('/dev/sda1', 'btrfs', '/', 'ro,relatime'), -+ result -+ ) -+ -+ @mock.patch('cloudinit.util.get_mount_info') -+ def test_mount_is_rw(self, m_mount_info): -+ m_mount_info.return_value = ('/dev/sda1', 'btrfs', '/', 'rw,relatime') -+ is_rw = util.mount_is_read_write('/') -+ self.assertEqual(is_rw, True) -+ -+ @mock.patch('cloudinit.util.get_mount_info') -+ def test_mount_is_ro(self, m_mount_info): -+ m_mount_info.return_value = ('/dev/sda1', 'btrfs', '/', 'ro,relatime') -+ is_rw = util.mount_is_read_write('/') -+ self.assertEqual(is_rw, False) + + class TestUtil(CiTestCase): + +@@ -44,3 +69,61 @@ class TestUtil(CiTestCase): + m_mount_info.return_value = ('/dev/sda1', 'btrfs', '/', 'ro,relatime') + is_rw = util.mount_is_read_write('/') + self.assertEqual(is_rw, False) + + @mock.patch('os.path.exists') + @mock.patch('cloudinit.util.load_file') @@ -132,7 +107,7 @@ + return 1 --- cloudinit/util.py.orig +++ cloudinit/util.py -@@ -570,6 +570,43 @@ def get_cfg_option_str(yobj, key, defaul +@@ -576,6 +576,43 @@ def get_cfg_option_str(yobj, key, defaul def get_cfg_option_int(yobj, key, default=0): return int(get_cfg_option_str(yobj, key, default=default)) @@ -176,7 +151,7 @@ def system_info(): info = { -@@ -578,19 +615,19 @@ def system_info(): +@@ -584,19 +621,19 @@ def system_info(): 'release': platform.release(), 'python': platform.python_version(), 'uname': platform.uname(), @@ -199,82 +174,9 @@ var = 'suse' else: var = 'linux' -@@ -2053,7 +2090,7 @@ def expand_package_list(version_fmt, pkg - return pkglist - - --def parse_mount_info(path, mountinfo_lines, log=LOG): -+def parse_mount_info(path, mountinfo_lines, log=LOG, get_mnt_opts=False): - """Return the mount information for PATH given the lines from - /proc/$$/mountinfo.""" - -@@ -2115,11 +2152,16 @@ def parse_mount_info(path, mountinfo_lin - - match_mount_point = mount_point - match_mount_point_elements = mount_point_elements -+ mount_options = parts[5] - -- if devpth and fs_type and match_mount_point: -- return (devpth, fs_type, match_mount_point) -+ if get_mnt_opts: -+ if devpth and fs_type and match_mount_point and mount_options: -+ return (devpth, fs_type, match_mount_point, mount_options) - else: -- return None -+ if devpth and fs_type and match_mount_point: -+ return (devpth, fs_type, match_mount_point) -+ -+ return None - - - def parse_mtab(path): -@@ -2189,7 +2231,7 @@ def parse_mount(path): - return None - - --def get_mount_info(path, log=LOG): -+def get_mount_info(path, log=LOG, get_mnt_opts=False): - # Use /proc/$$/mountinfo to find the device where path is mounted. - # This is done because with a btrfs filesystem using os.stat(path) - # does not return the ID of the device. -@@ -2221,7 +2263,7 @@ def get_mount_info(path, log=LOG): - mountinfo_path = '/proc/%s/mountinfo' % os.getpid() - if os.path.exists(mountinfo_path): - lines = load_file(mountinfo_path).splitlines() -- return parse_mount_info(path, lines, log) -+ return parse_mount_info(path, lines, log, get_mnt_opts) - elif os.path.exists("/etc/mtab"): - return parse_mtab(path) - else: -@@ -2329,7 +2371,8 @@ def pathprefix2dict(base, required=None, - missing.append(f) - - if len(missing): -- raise ValueError("Missing required files: %s", ','.join(missing)) -+ raise ValueError( -+ 'Missing required files: {files}'.format(files=','.join(missing))) - - return ret - -@@ -2606,4 +2649,10 @@ def wait_for_files(flist, maxwait, naple - return need - - -+def mount_is_read_write(mount_point): -+ """Check whether the given mount point is mounted rw""" -+ result = get_mount_info(mount_point, get_mnt_opts=True) -+ mount_opts = result[-1].split(',') -+ return mount_opts[0] == 'rw' -+ - # vi: ts=4 expandtab --- setup.py.orig +++ setup.py -@@ -1,3 +1,4 @@ -+ - # Copyright (C) 2009 Canonical Ltd. - # Copyright (C) 2012 Yahoo! Inc. - # -@@ -25,7 +26,7 @@ from distutils.errors import DistutilsAr +@@ -25,7 +25,7 @@ from distutils.errors import DistutilsAr import subprocess RENDERED_TMPD_PREFIX = "RENDERED_TEMPD" @@ -283,7 +185,7 @@ def is_f(p): return os.path.isfile(p) -@@ -114,10 +115,20 @@ def render_tmpl(template): +@@ -114,10 +114,20 @@ def render_tmpl(template): atexit.register(shutil.rmtree, tmpd) bname = os.path.basename(template).rstrip(tmpl_ext) fpath = os.path.join(tmpd, bname) @@ -305,7 +207,7 @@ INITSYS_FILES = { 'sysvinit': [f for f in glob('sysvinit/redhat/*') if is_f(f)], -@@ -227,6 +238,19 @@ if not in_virtualenv(): +@@ -227,6 +237,19 @@ if not in_virtualenv(): for k in INITSYS_ROOTS.keys(): INITSYS_ROOTS[k] = "/" + INITSYS_ROOTS[k] @@ -325,7 +227,7 @@ data_files = [ (ETC + '/cloud', [render_tmpl("config/cloud.cfg.tmpl")]), (ETC + '/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')), -@@ -259,7 +283,7 @@ requirements = read_requires() +@@ -259,7 +282,7 @@ requirements = read_requires() setuptools.setup( name='cloud-init', version=get_version(), diff --git a/cloud-init-python2-sigpipe.patch b/cloud-init-python2-sigpipe.patch index d418467..24d67e2 100644 --- a/cloud-init-python2-sigpipe.patch +++ b/cloud-init-python2-sigpipe.patch @@ -1,5 +1,3 @@ -Index: cloudinit/util.py -=================================================================== --- cloudinit/util.py.orig +++ cloudinit/util.py @@ -35,6 +35,7 @@ import time @@ -10,13 +8,13 @@ Index: cloudinit/util.py from six.moves.urllib import parse as urlparse import six -@@ -1858,7 +1859,8 @@ def subp(args, data=None, rcs=None, env= - +@@ -1868,7 +1869,8 @@ def subp(args, data=None, rcs=None, env= + try: sp = subprocess.Popen(args, stdout=stdout, stderr=stderr, stdin=stdin, - env=env, shell=shell) + env=env, shell=shell, + preexec_fn=lambda: signal(SIGPIPE, SIG_DFL)) (out, err) = sp.communicate(data) - - # Just ensure blank instead of none. + except OSError as e: + raise ProcessExecutionError( diff --git a/cloud-init-resize-ro-btrfs.patch b/cloud-init-resize-ro-btrfs.patch deleted file mode 100644 index 3a482ad..0000000 --- a/cloud-init-resize-ro-btrfs.patch +++ /dev/null @@ -1,20 +0,0 @@ ---- cloudinit/config/cc_resizefs.py -+++ cloudinit/config/cc_resizefs.py 2017/11/27 11:55:37 -@@ -59,7 +59,16 @@ - - - def _resize_btrfs(mount_point, devpth): -- return ('btrfs', 'filesystem', 'resize', 'max', mount_point) -+# That btrfs utilities are using a path and not a device is bad, -+# because the subvolume specified by '/' could be read-only -+# and btrfs will wrongly fail. Correct fix would be to mount the -+# real root of the filesystem (subvolid=5) and resize that. But -+# using /.snapshots as workaround is Ok, too, since this is -+# normally writeable. -+ if mount_point == '/' and os.path.isdir("/.snapshots"): -+ return ('btrfs', 'filesystem', 'resize', 'max', '/.snapshots') -+ else: -+ return ('btrfs', 'filesystem', 'resize', 'max', mount_point) - - - def _resize_ext(mount_point, devpth): diff --git a/cloud-init.changes b/cloud-init.changes index 58b0f8a..a02a94a 100644 --- a/cloud-init.changes +++ b/cloud-init.changes @@ -1,3 +1,67 @@ +------------------------------------------------------------------- +Wed Mar 21 22:27:40 UTC 2018 - rjschwei@suse.com + +- Update to version 18.1 (bsc#1085787, bsc#1084749) + + Forward port cloud-init-python2-sigpipe.patch + + Forward port 0003-Distro-dependent-chrony-config-file.patch + partial integration into 0001-Support-chrony-configuration-lp-1731619.patch + + Forward port cloud-init-no-python-linux-dist.patch + + Remove 0002-Disable-method-deprecation-warning-for-pylint.patch + use new cloud-init internal distro detection code + + Remove cloud-init-resize-ro-btrfs.patch included upstream + + Remove 0001-Set-syslog_fix_perms-for-SUSE-distro-addresses-bsc-1.patch + included upstream + + OVF: Fix VMware support for 64-bit platforms. [Sankar Tanguturi] + + ds-identify: Fix searching for iso9660 OVF cdroms. (LP: #1749980) + + SUSE: Fix groups used for ownership of cloud-init.log [Robert Schweikert] + + ds-identify: check /writable/system-data/ for nocloud seed. + (LP: #1747070) + + tests: run nosetests in cloudinit/ directory, fix py26 fallout. + + tools: run-centos: git clone rather than tar. + + tests: add support for logs with lxd from snap and future lxd 3. + (LP: #1745663) + + EC2: Fix get_instance_id called against cached datasource pickle. + (LP: #1748354) + + cli: fix cloud-init status to report running when before result.json + (LP: #1747965) + + net: accept network-config in netplan format for renaming interfaces + (LP: #1709715) + + Fix ssh keys validation in ssh_util [Tatiana Kholkina] + + docs: Update RTD content for cloud-init subcommands. + + OVF: Extend well-known labels to include OVFENV. (LP: #1698669) + + Fix potential cases of uninitialized variables. (LP: #1744796) + + tests: Collect script output as binary, collect systemd journal, fix lxd. + + HACKING.rst: mention setting user name and email via git config. + + Azure VM Preprovisioning support. [Douglas Jordan] (LP: #1734991) + + tools/read-version: Fix read-version when in a git worktree. + + docs: Fix typos in docs and one debug message. [Florian Grignon] + + btrfs: support resizing if root is mounted ro. + [Robert Schweikert] (LP: #1734787) + + OpenNebula: Improve network configuration support. + [Akihiko Ota] (LP: #1719157, #1716397, #1736750) + + tests: Fix EC2 Platform to return console output as bytes. + + tests: Fix attempted use of /run in a test case. + + GCE: Improvements and changes to ssh key behavior for default user. + [Max Illfelder] (LP: #1670456, #1707033, #1707037, #1707039) + + subp: make ProcessExecutionError have expected types in stderr, stdout. + + tests: when querying ntp server, do not do dns resolution. + + Recognize uppercase vfat disk labels [James Penick] (LP: #1598783) + + tests: remove zesty as supported OS to test [Joshua Powers] + + Do not log warning on config files that represent None. (LP: #1742479) + + tests: Use git hash pip dependency format for pylxd. + + tests: add integration requirements text file [Joshua Powers] + + MAAS: add check_instance_id based off oauth tokens. (LP: #1712680) + + tests: update apt sources list test [Joshua Powers] + + tests: clean up image properties [Joshua Powers] + + tests: rename test ssh keys to avoid appearance of leaking private keys. + [Joshua Powers] + + tests: Enable AWS EC2 Integration Testing [Joshua Powers] + + cli: cloud-init clean handles symlinks (LP: #1741093) + + SUSE: Add a basic test of network config rendering. [Robert Schweikert] + + Azure: Only bounce network when necessary. (LP: #1722668) + + lint: Fix lints seen by pylint version 1.8.1. + + cli: Fix error in cloud-init modules --mode=init. (LP: #1736600) + ------------------------------------------------------------------- Fri Feb 16 13:23:00 UTC 2018 - dmueller@suse.com diff --git a/cloud-init.spec b/cloud-init.spec index 2fa6f1d..551ca22 100644 --- a/cloud-init.spec +++ b/cloud-init.spec @@ -18,7 +18,7 @@ %global configver 0.7 Name: cloud-init -Version: 17.2 +Version: 18.1 Release: 0 License: GPL-3.0 and AGPL-3.0 Summary: Cloud node initialization tool @@ -37,15 +37,11 @@ Patch20: cloud-init-python2-sigpipe.patch Patch27: cloud-init-sysconfig-netpathfix.patch Patch29: datasourceLocalDisk.patch Patch34: cloud-init-tests-set-exec.patch -# FIXME cloud-init-resize-ro-btrfs.patch -# proposed for upstream merge (lp#1734787) -Patch36: cloud-init-resize-ro-btrfs.patch # FIXME chrony support upstream # These patches represent a working appraoch to supporting chrony # Upstream is seeking a significant re-write which is not likely to happen # before we need chrony support Patch37: 0001-Support-chrony-configuration-lp-1731619.patch -Patch38: 0002-Disable-method-deprecation-warning-for-pylint.patch Patch39: 0003-Distro-dependent-chrony-config-file.patch # FIXME switch to iproute2 tools # Proposed for merging upstream @@ -55,9 +51,6 @@ Patch40: 0001-switch-to-using-iproute2-tools.patch Patch41: cloud-init-no-python-linux-dist.patch # Disable OVF tests Patch42: cloud-init-skip-ovf-tests.patch -# FIXME do not use the default user:groups for log file permission settings -# Proposed for merging upstream -Patch43: 0001-Set-syslog_fix_perms-for-SUSE-distro-addresses-bsc-1.patch BuildRequires: fdupes BuildRequires: filesystem # pkg-config is needed to find correct systemd unit dir @@ -196,14 +189,11 @@ Documentation and examples for cloud-init tools %patch27 %patch29 -p0 %patch34 -%patch36 -%patch37 -p1 -%patch38 -p1 +%patch37 %patch39 -p1 %patch40 -p1 %patch41 %patch42 -%patch43 -p1 %build %if 0%{?suse_version} && 0%{?suse_version} <= 1315