Hello community, here is the log from the commit of package cloud-init for openSUSE:Factory checked in at 2017-11-03 16:28:01 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/cloud-init (Old) and /work/SRC/openSUSE:Factory/.cloud-init.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "cloud-init" Fri Nov 3 16:28:01 2017 rev:40 rq:538365 version:17.1 Changes: -------- --- /work/SRC/openSUSE:Factory/cloud-init/cloud-init.changes 2017-09-20 17:14:13.332622059 +0200 +++ /work/SRC/openSUSE:Factory/.cloud-init.new/cloud-init.changes 2017-11-03 16:29:18.070490741 +0100 @@ -1,0 +2,558 @@ +Thu Nov 2 13:16:49 UTC 2017 - [email protected] + +- Fix sed expression to set distro properly (boo#1063716) + +------------------------------------------------------------------- +Thu Sep 21 17:32:55 EDT 2017 - [email protected] + +- Update to version 17.1 (bsc#1035106) + + Version numbering scheme change now YY.NUMBER_OF_RELESE_THAT_YEAR + + Remove cloud.cfg.suse, use generated default config file + + Remove addopenSUSEBase.patch, included upstream + + Remove suseIntegratedHandler.patch, included upstream + + Remove openSUSEhostsTemplate.diff, included upstream + + Remove cloud-init-handle-no-carrier.patch, included upstream + + Remove cloud-init-digital-ocean-datasource.patch, + use upstream implementation + + Remove cloud-init-digital-ocean-datasource-enable-by-default.patch, + use upstream implementation + + Remove cloud-init-fix-unicode-handling-binarydecode.patch, + included upstream + + Remove cloud-init-no-dmidecode-on-ppc64.patch, included upstream + + Remove dataSourceOpenNebula.patch, use upstream implementation + + Remove setupSUSEsysVInit.diff, included upstream + + Remove suseSysVInit.diff, included upstream + + Remove cloud-init-finalbeforelogin.patch, don't block login + + Remove cloud-init-handle-not-implemented-query.patch, query option removed + + Remove cloud-init-spceandtabs-clean.patch, indentation fixed upstream + + Remove dynamicInitCmd.diff, different solution from upstream + + Added cloud-init-more-tasks.patch, (bsc#1047363) + replace cloud-init-finalbeforelogin.patch + + Forward port cloud-init-python2-sigpipe.patch + + Remove cloud-init-net-eni.patch, included upstream + + Remove cloud-init-service.patch, included upstream + + Forward port cloud-init-sysconfig-netpathfix.patch + + Remove cloud-init-net-sysconfig-lp1665441.patch, included upstream + + Remove cloud-init-python26.patch, included upstream + + Remove skip-argparse-on-python3.patch + + Add cloud-init-tests-set-exec.patch + + Add cloud-init-final-no-apt.patch + + Add zypp_add_repo_test.patch + + doc: document GCE datasource. [Arnd Hannemann] + + suse: updates to templates to support openSUSE and SLES. + + [Robert Schweikert] (LP: #1718640) + + suse: Copy sysvinit files from redhat with slight changes. + + [Robert Schweikert] (LP: #1718649) + + docs: fix sphinx module schema documentation [Chad Smith] + + tests: Add cloudinit package to all test targets [Chad Smith] + + Makefile: No longer look for yaml files in obsolete ./bin/. + + tests: fix ds-identify unit tests to set EC2_STRICT_ID_DEFAULT. + + ec2: Fix maybe_perform_dhcp_discovery to use /var/tmp as a tmpdir + + [Chad Smith] (LP: #1717627) + + Azure: wait longer for SSH pub keys to arrive. + + [Paul Meyer] (LP: #1717611) + + GCE: Fix usage of user-data. (LP: #1717598) + + cmdline: add collect-logs subcommand. [Chad Smith] (LP: #1607345) + + CloudStack: consider dhclient lease files named with a hyphen. + + (LP: #1717147) + + resizefs: Drop check for read-only device file, do not warn on + + overlayroot. [Chad Smith] + + Do not provide systemd-fsck drop-in which could cause ordering cycles. + + [Balint Reczey] (LP: #1717477) + + tests: Enable the NoCloud KVM platform [Joshua Powers] + + resizefs: pass mount point to xfs_growfs [Dusty Mabe] + + vmware: Enable nics before sending the SUCCESS event. [Sankar Tanguturi] + + cloud-config modules: honor distros definitions in each module + + [Chad Smith] (LP: #1715738, #1715690) + + chef: Add option to pin chef omnibus install version + + [Ethan Apodaca] (LP: #1462693) + + tests: execute: support command as string [Joshua Powers] + + schema and docs: Add jsonschema to resizefs and bootcmd modules + + [Chad Smith] + + tools: Add xkvm script, wrapper around qemu-system [Joshua Powers] + + vmware customization: return network config format + + [Sankar Tanguturi] (LP: #1675063) + + Ec2: only attempt to operate at local mode on known platforms. + + (LP: #1715128) + + Use /run/cloud-init for tempfile operations. (LP: #1707222) + + ds-identify: Make OpenStack return maybe on arch other than intel. + + (LP: #1715241) + + tests: mock missed openstack metadata uri network_data.json + + [Chad Smith] (LP: #1714376) + + relocate tests/unittests/helpers.py to cloudinit/tests + + [Lars Kellogg-Stedman] + + tox: add nose timer output [Joshua Powers] + + upstart: do not package upstart jobs, drop ubuntu-init-switch module. + + tests: Stop leaking calls through unmocked metadata addresses + + [Chad Smith] (LP: #1714117) + + distro: allow distro to specify a default locale [Ryan Harper] + + tests: fix two recently added tests for sles distro. + + url_helper: dynamically import oauthlib import from inside oauth_headers + + [Chad Smith] + + tox: make xenial environment run with python3.6 + + suse: Add support for openSUSE and return SLES to a working state. + + [Robert Schweikert] + + GCE: Add a main to the GCE Datasource. + + ec2: Add IPv6 dhcp support to Ec2DataSource. [Chad Smith] (LP: #1639030) + + url_helper: fail gracefully if oauthlib is not available + + [Lars Kellogg-Stedman] (LP: #1713760) + + cloud-init analyze: fix issues running under python 2. [Andrew Jorgensen] + + Configure logging module to always use UTC time. + + [Ryan Harper] (LP: #1713158) + + Log a helpful message if a user script does not include shebang. + + [Andrew Jorgensen] + + cli: Fix command line parsing of coniditionally loaded subcommands. + + [Chad Smith] (LP: #1712676) + + doc: Explain error behavior in user data include file format. + + [Jason Butz] + + cc_landscape & cc_puppet: Fix six.StringIO use in writing configs + + [Chad Smith] (LP: #1699282, #1710932) + + schema cli: Add schema subcommand to cloud-init cli and cc_runcmd schema + + [Chad Smith] + + Debian: Remove non-free repositories from apt sources template. + + [Joonas Kylmälä] (LP: #1700091) + + tools: Add tooling for basic cloud-init performance analysis. + + [Chad Smith] (LP: #1709761) + + network: add v2 passthrough and fix parsing v2 config with bonds/bridge + + params [Ryan Harper] (LP: #1709180) + + doc: update capabilities with features available, link doc reference, + + cli example [Ryan Harper] + + vcloud directory: Guest Customization support for passwords + + [Maitreyee Saikia] + + ec2: Allow Ec2 to run in init-local using dhclient in a sandbox. + + [Chad Smith] (LP: #1709772) + + cc_ntp: fallback on timesyncd configuration if ntp is not installable + + [Ryan Harper] (LP: #1686485) + + net: Reduce duplicate code. Have get_interfaces_by_mac use + + get_interfaces. + + tests: Fix build tree integration tests [Joshua Powers] + + sysconfig: Dont repeat header when rendering resolv.conf + + [Ryan Harper] (LP: #1701420) + + archlinux: Fix bug with empty dns, do not render 'lo' devices. + + (LP: #1663045, #1706593) + + cloudinit.net: add initialize_network_device function and tests + + [Chad Smith] + + makefile: fix ci-deps-ubuntu target [Chad Smith] + + tests: adjust locale integration test to parse default locale. + + tests: remove 'yakkety' from releases as it is EOL. + + tests: Add initial tests for EC2 and improve a docstring. + + locale: Do not re-run locale-gen if provided locale is system default. + + archlinux: fix set hostname usage of write_file. + + [Joshua Powers] (LP: #1705306) + + sysconfig: support subnet type of 'manual'. + + tools/run-centos: make running with no argument show help. + + Drop rand_str() usage in DNS redirection detection + + [Bob Aman] (LP: #1088611) + + sysconfig: use MACADDR on bonds/bridges to configure mac_address + + [Ryan Harper] (LP: #1701417) + + net: eni route rendering missed ipv6 default route config + + [Ryan Harper] (LP: #1701097) + + sysconfig: enable mtu set per subnet, including ipv6 mtu + + [Ryan Harper] (LP: #1702513) + + sysconfig: handle manual type subnets [Ryan Harper] (LP: #1687725) + + sysconfig: fix ipv6 gateway routes [Ryan Harper] (LP: #1694801) + + sysconfig: fix rendering of bond, bridge and vlan types. + + [Ryan Harper] (LP: #1695092) + + Templatize systemd unit files for cross distro deltas. [Ryan Harper] + + sysconfig: ipv6 and default gateway fixes. [Ryan Harper] (LP: #1704872) + + net: fix renaming of nics to support mac addresses written in upper + + case. (LP: #1705147) + + tests: fixes for issues uncovered when moving to python 3.6. + + (LP: #1703697) + + sysconfig: include GATEWAY value if set in subnet + + [Ryan Harper] (LP: #1686856) + + Scaleway: add datasource with user and vendor data for Scaleway. + + [Julien Castets] + + Support comments in content read by load_shell_content. + + cloudinitlocal fail to run during boot [Hongjiang Zhang] + + doc: fix disk setup example table_type options + + [Sandor Zeestraten] (LP: #1703789) + + tools: Fix exception handling. [Joonas Kylmälä] (LP: #1701527) + + tests: fix usage of mock in GCE test. + + test_gce: Fix invalid mock of platform_reports_gce to return False + + [Chad Smith] + + test: fix incorrect keyid for apt repository. + + [Joshua Powers] (LP: #1702717) + + tests: Update version of pylxd [Joshua Powers] + + write_files: Remove log from helper function signatures. + + [Andrew Jorgensen] + + doc: document the cmdline options to NoCloud [Brian Candler] + + read_dmi_data: always return None when inside a container. (LP: #1701325) + + requirements.txt: remove trailing white space. + + Azure: Add network-config, Refactor net layer to handle duplicate macs. + + [Ryan Harper] + + Tests: Simplify the check on ssh-import-id [Joshua Powers] + + tests: update ntp tests after sntp added [Joshua Powers] + + FreeBSD: Make freebsd a variant, fix unittests and + + tools/build-on-freebsd. + + FreeBSD: fix test failure + + FreeBSD: replace ifdown/ifup with "ifconfig down" and "ifconfig up". + + [Hongjiang Zhang] (LP: #1697815) + + FreeBSD: fix cdrom mounting failure if /mnt/cdrom/secure did not exist. + + [Hongjiang Zhang] (LP: #1696295) + + main: Don't use templater to format the welcome message + + [Andrew Jorgensen] + + docs: Automatically generate module docs form schema if present. + + [Chad Smith] + + debian: fix path comment in /etc/hosts template. ++++ 361 more lines (skipped) ++++ between /work/SRC/openSUSE:Factory/cloud-init/cloud-init.changes ++++ and /work/SRC/openSUSE:Factory/.cloud-init.new/cloud-init.changes Old: ---- addopenSUSEBase.patch cloud-init-0.7.8.tar.gz cloud-init-digital-ocean-datasource-enable-by-default.patch cloud-init-digital-ocean-datasource.patch cloud-init-finalbeforelogin.patch cloud-init-fix-unicode-handling-binarydecode.patch cloud-init-handle-no-carrier.patch cloud-init-handle-not-implemented-query.patch cloud-init-net-eni.patch cloud-init-net-sysconfig-lp1665441.patch cloud-init-no-dmidecode-on-ppc64.patch cloud-init-python26.patch cloud-init-service.patch cloud-init-spceandtabs-clean.patch cloud.cfg.suse dataSourceOpenNebula.patch dynamicInitCmd.diff openSUSEhostsTemplate.diff setupSUSEsysVInit.diff skip-argparse-on-python3.patch suseIntegratedHandler.patch suseSysVInit.diff New: ---- cloud-init-17.1.tar.gz cloud-init-final-no-apt.patch cloud-init-more-tasks.patch cloud-init-tests-set-exec.patch zypp_add_repo_test.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ cloud-init.spec ++++++ --- /var/tmp/diff_new_pack.jeiljh/_old 2017-11-03 16:29:19.098453326 +0100 +++ /var/tmp/diff_new_pack.jeiljh/_new 2017-11-03 16:29:19.102453181 +0100 @@ -18,73 +18,60 @@ %global configver 0.7 Name: cloud-init -Version: 0.7.8 +Version: 17.1 Release: 0 License: GPL-3.0 and AGPL-3.0 Summary: Cloud node initialization tool Url: http://launchpad.net/cloud-init/ Group: System/Management Source0: %{name}-%{version}.tar.gz -Source1: cloud.cfg.suse -Source2: rsyslog-cloud-init.cfg -Patch0: suseSysVInit.diff -# FIXME addopenSUSEBase.patch proposed for upstream merge -Patch1: addopenSUSEBase.patch -# FIXME suseIntegratedHandler.patch proposed for upstream merge -Patch2: suseIntegratedHandler.patch -Patch3: setupSUSEsysVInit.diff -# FIXME openSUSEhostsTemplate.diff proposed for upstream merge -Patch5: openSUSEhostsTemplate.diff -# FIXME dynamicInitCmd.patch proposed for upstream merge -Patch6: dynamicInitCmd.diff -Patch9: cloud-init-no-dmidecode-on-ppc64.patch +Source1: rsyslog-cloud-init.cfg +# FIXME zypp_add_repos.diff needs proposed for upstream merge +Patch4: zypp_add_repos.diff +Patch5: zypp_add_repo_test.patch Patch10: cloud-init-no-user-lock-if-already-locked.patch -Patch11: dataSourceOpenNebula.patch Patch12: fix-default-systemd-unit-dir.patch -Patch14: cloud-init-finalbeforelogin.patch +# FIXME cloud-init-more-tasks.patch proposed for upstream merge +Patch13: cloud-init-more-tasks.patch # python2 disables SIGPIPE, causing broken pipe errors in shell scripts (bsc#903449) Patch20: cloud-init-python2-sigpipe.patch -Patch21: cloud-init-net-eni.patch -Patch22: cloud-init-service.patch -Patch23: cloud-init-fix-unicode-handling-binarydecode.patch -# From upstream patch -Patch24: cloud-init-handle-no-carrier.patch -Patch25: cloud-init-digital-ocean-datasource.patch -Patch26: cloud-init-digital-ocean-datasource-enable-by-default.patch Patch27: cloud-init-sysconfig-netpathfix.patch -Patch28: zypp_add_repos.diff Patch29: datasourceLocalDisk.patch -Patch30: cloud-init-handle-not-implemented-query.patch -Patch32: cloud-init-net-sysconfig-lp1665441.patch -Patch33: cloud-init-spceandtabs-clean.patch -Patch34: skip-argparse-on-python3.patch +Patch34: cloud-init-tests-set-exec.patch +Patch35: cloud-init-final-no-apt.patch BuildRequires: fdupes BuildRequires: filesystem -%if 0%{?suse_version} && 0%{?suse_version} > 1315 -BuildRequires: python3-devel -BuildRequires: python3-setuptools -%else -BuildRequires: python-devel -BuildRequires: python-setuptools -%endif # pkg-config is needed to find correct systemd unit dir BuildRequires: pkg-config # needed for /lib/udev BuildRequires: udev %if 0%{?suse_version} > 1320 +BuildRequires: python3-devel +BuildRequires: python3-setuptools # Test requirements -#BuildRequires: python3-Cheetah BuildRequires: python3-Jinja2 BuildRequires: python3-PrettyTable BuildRequires: python3-PyYAML -BuildRequires: python3-configobj -#BuildRequires: python3-contextlib2 +BuildRequires: python3-configobj >= 5.0.2 BuildRequires: python3-httpretty BuildRequires: python3-jsonpatch BuildRequires: python3-mock +BuildRequires: python3-nose BuildRequires: python3-oauthlib BuildRequires: python3-requests BuildRequires: python3-testtools +%else +BuildRequires: python-devel +BuildRequires: python-Jinja2 +BuildRequires: python-PyYAML +BuildRequires: python-requests +BuildRequires: python-setuptools +BuildRequires: python-six +%endif +%if 0%{?is_opensuse} || 0%{?suse_version} == 1310 || 0%{?suse_version} == 1320 +BuildRequires: openSUSE-release +%else +BuildRequires: sles-release %endif Requires: bash Requires: file @@ -97,9 +84,10 @@ Requires: openssh %if 0%{?suse_version} > 1320 Requires: python3-boto >= 2.7 -Requires: python3-configobj +Requires: python3-configobj >= 5.0.2 Requires: python3-Jinja2 Requires: python3-jsonpatch +Requires: python3-jsonschema Requires: python3-oauthlib Requires: python3-PrettyTable Requires: python3-pyserial @@ -111,9 +99,10 @@ %else Requires: python-argparse Requires: python-boto >= 2.7 -Requires: python-configobj +Requires: python-configobj >= 5.0.2 Requires: python-Jinja2 Requires: python-jsonpatch +Requires: python-jsonschema Requires: python-oauthlib Requires: python-PrettyTable Requires: python-pyserial @@ -140,7 +129,6 @@ %endif %if 0%{?suse_version} && 0%{?suse_version} <= 1210 %define initsys sysvinit_suse -Patch40: cloud-init-python26.patch %else %define initsys systemd BuildRequires: systemd @@ -187,52 +175,30 @@ Documentation and examples for cloud-init tools -%package test -Summary: Cloud node initialization tool - Testsuite -Group: System/Management -Requires: cloud-init = %{version} - -%description test -Cloud-init is an init script that initializes a cloud node (VM) -according to the fetched configuration data from the admin node. - -Unit tests for the cloud-init tools +#%package test +#Summary: Cloud node initialization tool - Testsuite +#Group: System/Management +#Requires: cloud-init = %{version} +# +#%description test +#Cloud-init is an init script that initializes a cloud node (VM) +#according to the fetched configuration data from the admin node. +# +#Unit tests for the cloud-init tools %prep %setup -q -%patch0 -p1 -%patch1 -p1 -%patch2 -%patch3 -%patch5 -%patch6 -%patch9 +%patch4 -p0 +%patch5 -p0 %patch10 -p1 -%patch11 %patch12 -%patch14 +%patch13 %patch20 -%patch21 -%patch22 -%patch23 -%patch24 -%patch25 -p1 -%patch26 -p1 %patch27 -%patch28 -p0 %patch29 -p0 -%patch30 -%patch32 -p1 -%patch33 -p1 %patch34 -%if 0%{?suse_version} && 0%{?suse_version} <= 1210 -%patch40 -p1 -%endif +%patch35 -p1 -%if 0%{?suse_version} <= 1130 -# disable ecdsa for SLE 11 (not available) -echo "ssh_genkeytypes: ['rsa', 'dsa']" >> %{SOURCE1} -%endif %build %if 0%{?suse_version} && 0%{?suse_version} <= 1315 @@ -247,16 +213,11 @@ # these tests are currently failing due to suse patches rm -v tests/unittests/test_distros/test_netconfig.py rm -v tests/unittests/test_net.py +# Ignore test failure currently not doing anything with opennebula rm -v tests/unittests/test_datasource/test_opennebula.py -rm -v tests/unittests/test_datasource/test_cloudstack.py -# These tests fail in python 3 -rm -v tests/unittests/test_datasource/test_altcloud.py -rm -v tests/unittests/test_handler/test_handler_apt_source_v3.py -%if 0%{?suse_version} && 0%{?suse_version} <= 1315 -python -m testtools.run -%else -python3 -m testtools.run -%endif +# To be investigated +rm -v tests/unittests/test_handler/test_handler_ntp.py +make test %endif @@ -275,28 +236,30 @@ # move documentation mkdir -p %{buildroot}%{_defaultdocdir} mv %{buildroot}%{_datadir}/doc/%{name} %{buildroot}%{docdir} -cp -a %{SOURCE1} %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg +%if 0%{?suse_version} <= 1130 +# disable ecdsa for SLE 11 (not available) +echo "ssh_genkeytypes: ['rsa', 'dsa']" >> %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg +%endif # copy the LICENSE cp LICENSE %{buildroot}%{docdir} # Set the distribution indicator %if 0%{?suse_version} %if 0%{?suse_version} < 1130 #SLE 11, openSUSE 11.x is EOL -sed -i s/INSERT_SUSE_DISTRO/sles/ %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg +sed -i s/suse/sles/ %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg %endif %if 0%{?suse_version} > 1140 -%if 0%{?suse_version} == 1315 -# SLE 12 -sed -i s/INSERT_SUSE_DISTRO/sles/ %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg +%if 0%{?is_opensuse} +sed -i s/suse/opensuse/ %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg %else -sed -i s/INSERT_SUSE_DISTRO/opensuse/ %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg +sed -i s/suse/sles/ %{buildroot}/%{_sysconfdir}/cloud/cloud.cfg %endif %endif %endif %if 0%{?suse_version} && 0%{?suse_version} > 1110 mkdir -p %{buildroot}/%{_sysconfdir}/rsyslog.d mkdir -p %{buildroot}/usr/lib/udev/rules.d/ -cp -a %{SOURCE2} %{buildroot}/%{_sysconfdir}/rsyslog.d/21-cloudinit.conf +cp -a %{SOURCE1} %{buildroot}/%{_sysconfdir}/rsyslog.d/21-cloudinit.conf mv %{buildroot}/lib/udev/rules.d/66-azure-ephemeral.rules %{buildroot}/usr/lib/udev/rules.d/ %endif @@ -396,15 +359,11 @@ %{docdir}/examples/* %{docdir}/README %{docdir}/*.txt -%{docdir}/*.rst +#%{docdir}/*.rst %dir %{docdir}/examples -%files test -%defattr(-,root,root) -%if 0%{?suse_version} && 0%{?suse_version} <= 1315 -%{python_sitelib}/tests -%else -%{python3_sitelib}/tests -%endif +#%files test +#%defattr(-,root,root) +#%{python_sitelib}/tests %changelog ++++++ cloud-init-0.7.8.tar.gz -> cloud-init-17.1.tar.gz ++++++ ++++ 59611 lines of diff (skipped) ++++++ cloud-init-final-no-apt.patch ++++++ Index: cloud-init-17.1/systemd/cloud-final.service.tmpl =================================================================== --- cloud-init-17.1.orig/systemd/cloud-final.service.tmpl +++ cloud-init-17.1/systemd/cloud-final.service.tmpl @@ -4,9 +4,9 @@ Description=Execute cloud user/final scr After=network-online.target cloud-config.service rc-local.service {% if variant in ["ubuntu", "unknown", "debian"] %} After=multi-user.target +Before=apt-daily.service {% endif %} Wants=network-online.target cloud-config.service -Before=apt-daily.service [Service] Type=oneshot ++++++ cloud-init-more-tasks.patch ++++++ --- systemd/cloud-final.service.tmpl.orig +++ systemd/cloud-final.service.tmpl @@ -14,6 +14,7 @@ ExecStart=/usr/bin/cloud-init modules -- RemainAfterExit=yes TimeoutSec=0 KillMode=process +TasksMax=infinity # Output needs to appear in instance console output StandardOutput=journal+console ++++++ cloud-init-python2-sigpipe.patch ++++++ --- /var/tmp/diff_new_pack.jeiljh/_old 2017-11-03 16:29:19.430441243 +0100 +++ /var/tmp/diff_new_pack.jeiljh/_new 2017-11-03 16:29:19.430441243 +0100 @@ -1,20 +1,20 @@ --- cloudinit/util.py.orig +++ cloudinit/util.py -@@ -46,6 +46,7 @@ import tempfile - import time +@@ -35,6 +35,7 @@ import time + from errno import ENOENT, ENOEXEC from base64 import b64decode, b64encode +from signal import signal, SIGPIPE, SIG_DFL from six.moves.urllib import parse as urlparse import six -@@ -1802,7 +1803,8 @@ def subp(args, data=None, rcs=None, env= +@@ -1815,7 +1816,8 @@ def subp(args, data=None, rcs=None, env= 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)) ++ preexec_fn=lambda: signal(SIGPIPE, SIG_DFL)) (out, err) = sp.communicate(data) # Just ensure blank instead of none. ++++++ cloud-init-sysconfig-netpathfix.patch ++++++ --- /var/tmp/diff_new_pack.jeiljh/_old 2017-11-03 16:29:19.442440806 +0100 +++ /var/tmp/diff_new_pack.jeiljh/_new 2017-11-03 16:29:19.442440806 +0100 @@ -1,15 +1,17 @@ --- cloudinit/net/sysconfig.py.orig +++ cloudinit/net/sysconfig.py -@@ -94,7 +94,7 @@ class ConfigMap(object): +@@ -88,8 +88,8 @@ class ConfigMap(object): class Route(ConfigMap): """Represents a route configuration.""" -- route_fn_tpl = '%(base)s/network-scripts/route-%(name)s' -+ route_fn_tpl = '%(base)s/network/route-%(name)s' +- route_fn_tpl_ipv4 = '%(base)s/network-scripts/route-%(name)s' +- route_fn_tpl_ipv6 = '%(base)s/network-scripts/route6-%(name)s' ++ route_fn_tpl_ipv4 = '%(base)s/network/route-%(name)s' ++ route_fn_tpl_ipv6 = '%(base)s/network/route6-%(name)s' def __init__(self, route_name, base_sysconf_dir): super(Route, self).__init__() -@@ -119,7 +119,7 @@ class Route(ConfigMap): +@@ -166,7 +166,7 @@ class Route(ConfigMap): class NetInterface(ConfigMap): """Represents a sysconfig/networking-script (and its config + children).""" @@ -18,3 +20,14 @@ iface_types = { 'ethernet': 'Ethernet', +@@ -602,8 +602,8 @@ def available(target=None): + return False + + expected_paths = [ +- 'etc/sysconfig/network-scripts/network-functions', +- 'etc/sysconfig/network-scripts/ifdown-eth'] ++ 'etc/sysconfig/network/network-functions', ++ 'etc/sysconfig/network/ifdown-eth'] + for p in expected_paths: + if not os.path.isfile(util.target_path(target, p)): + return False ++++++ cloud-init-tests-set-exec.patch ++++++ --- Makefile.orig +++ Makefile @@ -11,7 +11,7 @@ PIP_INSTALL := pip install ifeq ($(PYVER),python3) pyflakes = pyflakes3 - unittests = unittest3 + unittests = unittest yaml = yaml else ifeq ($(PYVER),python2) @@ -19,7 +19,7 @@ ifeq ($(PYVER),python2) unittests = unittest else pyflakes = pyflakes pyflakes3 - unittests = unittest unittest3 + unittests = unittest unittest endif endif ++++++ fix-default-systemd-unit-dir.patch ++++++ --- /var/tmp/diff_new_pack.jeiljh/_old 2017-11-03 16:29:19.466439932 +0100 +++ /var/tmp/diff_new_pack.jeiljh/_new 2017-11-03 16:29:19.466439932 +0100 @@ -1,6 +1,6 @@ --- setup.py.orig +++ setup.py -@@ -58,8 +58,8 @@ def tiny_p(cmd, capture=True): +@@ -49,8 +49,8 @@ def tiny_p(cmd, capture=True): def pkg_config_read(library, var): fallbacks = { 'systemd': { @@ -13,9 +13,9 @@ cmd = ['pkg-config', '--variable=%s' % var, library] --- systemd/cloud-init-generator.orig +++ systemd/cloud-init-generator -@@ -7,7 +7,7 @@ LOG_D="/run/cloud-init" - ENABLE="enabled" - DISABLE="disabled" +@@ -9,7 +9,7 @@ DISABLE="disabled" + FOUND="found" + NOTFOUND="notfound" RUN_ENABLED_FILE="$LOG_D/$ENABLE" -CLOUD_SYSTEM_TARGET="/lib/systemd/system/cloud-init.target" +CLOUD_SYSTEM_TARGET="/usr/lib/systemd/system/cloud-init.target" ++++++ zypp_add_repo_test.patch ++++++ Index: tests/unittests/test_handler/test_handler_zypper_add_repo.py =================================================================== --- /dev/null +++ tests/unittests/test_handler/test_handler_zypper_add_repo.py @@ -0,0 +1,238 @@ + +# This file is part of cloud-init. See LICENSE file for license information. + +import glob +import os + +from cloudinit.config import cc_zypper_add_repo +from cloudinit import util + +from cloudinit.tests import helpers +from cloudinit.tests.helpers import mock + +try: + from configparser import ConfigParser +except ImportError: + from ConfigParser import ConfigParser +import logging +from six import StringIO + +LOG = logging.getLogger(__name__) + + +class TestConfig(helpers.FilesystemMockingTestCase): + def setUp(self): + super(TestConfig, self).setUp() + self.tmp = self.tmp_dir() + self.zypp_conf = 'etc/zypp/zypp.conf' + + def test_bad_repo_config(self): + """Config has no baseurl, no file should be written""" + cfg = { + 'repos': [ + { + 'id': 'foo', + 'name': 'suse-test', + 'enabled': '1' + }, + ] + } + self.patchUtils(self.tmp) + cc_zypper_add_repo._write_repos(cfg['repos'], '/etc/zypp/repos.d') + self.assertRaises(IOError, util.load_file, + "/etc/zypp/repos.d/foo.repo") + + def test_write_repos(self): + """Verify valid repos get written""" + cfg = self._get_base_config_repos() + root_d = self.tmp_dir() + cc_zypper_add_repo._write_repos(cfg['zypper']['repos'], root_d) + repos = glob.glob('%s/*.repo' % root_d) + expected_repos = ['testing-foo.repo', 'testing-bar.repo'] + if len(repos) != 2: + assert 'Number of repos written is "%d" expected 2' % len(repos) + for repo in repos: + repo_name = os.path.basename(repo) + if repo_name not in expected_repos: + assert 'Found repo with name "%s"; unexpected' % repo_name + # Validation that the content gets properly written is in another test + + def test_write_repo(self): + """Verify the content of a repo file""" + cfg = { + 'repos': [ + { + 'baseurl': 'http://foo', + 'name': 'test-foo', + 'id': 'testing-foo' + }, + ] + } + root_d = self.tmp_dir() + cc_zypper_add_repo._write_repos(cfg['repos'], root_d) + contents = util.load_file("%s/testing-foo.repo" % root_d) + parser = ConfigParser() + parser.readfp(StringIO(contents)) + expected = { + 'testing-foo': { + 'name': 'test-foo', + 'baseurl': 'http://foo', + 'enabled': '1', + 'autorefresh': '1' + } + } + for section in expected: + self.assertTrue(parser.has_section(section), + "Contains section {0}".format(section)) + for k, v in expected[section].items(): + self.assertEqual(parser.get(section, k), v) + + def test_config_write(self): + """Write valid configuration data""" + cfg = { + 'config': { + 'download.deltarpm': 'False', + 'reposdir': 'foo' + } + } + root_d = self.tmp_dir() + helpers.populate_dir(root_d, {self.zypp_conf: '# Zypp config\n'}) + self.reRoot(root_d) + cc_zypper_add_repo._write_zypp_config(cfg['config']) + cfg_out = os.path.join(root_d, self.zypp_conf) + contents = util.load_file(cfg_out) + expected = [ + '# Zypp config', + '# Added via cloud.cfg', + 'download.deltarpm=False', + 'reposdir=foo' + ] + for item in contents.split('\n'): + if item not in expected: + self.assertIsNone(item) + + @mock.patch('cloudinit.log.logging') + def test_config_write_skip_configdir(self, mock_logging): + """Write configuration but skip writing 'configdir' setting""" + cfg = { + 'config': { + 'download.deltarpm': 'False', + 'reposdir': 'foo', + 'configdir': 'bar' + } + } + root_d = self.tmp_dir() + helpers.populate_dir(root_d, {self.zypp_conf: '# Zypp config\n'}) + self.reRoot(root_d) + cc_zypper_add_repo._write_zypp_config(cfg['config']) + cfg_out = os.path.join(root_d, self.zypp_conf) + contents = util.load_file(cfg_out) + expected = [ + '# Zypp config', + '# Added via cloud.cfg', + 'download.deltarpm=False', + 'reposdir=foo' + ] + for item in contents.split('\n'): + if item not in expected: + self.assertIsNone(item) + # Not finding teh right path for mocking :( + # assert mock_logging.warning.called + + def test_empty_config_section_no_new_data(self): + """When the config section is empty no new data should be written to + zypp.conf""" + cfg = self._get_base_config_repos() + cfg['zypper']['config'] = None + root_d = self.tmp_dir() + helpers.populate_dir(root_d, {self.zypp_conf: '# No data'}) + self.reRoot(root_d) + cc_zypper_add_repo._write_zypp_config(cfg.get('config', {})) + cfg_out = os.path.join(root_d, self.zypp_conf) + contents = util.load_file(cfg_out) + self.assertEqual(contents, '# No data') + + def test_empty_config_value_no_new_data(self): + """When the config section is not empty but there are no values + no new data should be written to zypp.conf""" + cfg = self._get_base_config_repos() + cfg['zypper']['config'] = { + 'download.deltarpm': None + } + root_d = self.tmp_dir() + helpers.populate_dir(root_d, {self.zypp_conf: '# No data'}) + self.reRoot(root_d) + cc_zypper_add_repo._write_zypp_config(cfg.get('config', {})) + cfg_out = os.path.join(root_d, self.zypp_conf) + contents = util.load_file(cfg_out) + self.assertEqual(contents, '# No data') + + def test_handler_full_setup(self): + """Test that the handler ends up calling the renderers""" + cfg = self._get_base_config_repos() + cfg['zypper']['config'] = { + 'download.deltarpm': 'False', + } + root_d = self.tmp_dir() + os.makedirs('%s/etc/zypp/repos.d' % root_d) + helpers.populate_dir(root_d, {self.zypp_conf: '# Zypp config\n'}) + self.reRoot(root_d) + cc_zypper_add_repo.handle('zypper_add_repo', cfg, None, LOG, []) + cfg_out = os.path.join(root_d, self.zypp_conf) + contents = util.load_file(cfg_out) + expected = [ + '# Zypp config', + '# Added via cloud.cfg', + 'download.deltarpm=False', + ] + for item in contents.split('\n'): + if item not in expected: + self.assertIsNone(item) + repos = glob.glob('%s/etc/zypp/repos.d/*.repo' % root_d) + expected_repos = ['testing-foo.repo', 'testing-bar.repo'] + if len(repos) != 2: + assert 'Number of repos written is "%d" expected 2' % len(repos) + for repo in repos: + repo_name = os.path.basename(repo) + if repo_name not in expected_repos: + assert 'Found repo with name "%s"; unexpected' % repo_name + + def test_no_config_section_no_new_data(self): + """When there is no config section no new data should be written to + zypp.conf""" + cfg = self._get_base_config_repos() + root_d = self.tmp_dir() + helpers.populate_dir(root_d, {self.zypp_conf: '# No data'}) + self.reRoot(root_d) + cc_zypper_add_repo._write_zypp_config(cfg.get('config', {})) + cfg_out = os.path.join(root_d, self.zypp_conf) + contents = util.load_file(cfg_out) + self.assertEqual(contents, '# No data') + + def test_no_repo_data(self): + """When there is no repo data nothing should happen""" + root_d = self.tmp_dir() + self.reRoot(root_d) + cc_zypper_add_repo._write_repos(None, root_d) + content = glob.glob('%s/*' % root_d) + self.assertEqual(len(content), 0) + + def _get_base_config_repos(self): + """Basic valid repo configuration""" + cfg = { + 'zypper': { + 'repos': [ + { + 'baseurl': 'http://foo', + 'name': 'test-foo', + 'id': 'testing-foo' + }, + { + 'baseurl': 'http://bar', + 'name': 'test-bar', + 'id': 'testing-bar' + } + ] + } + } + return cfg Index: tests/unittests/test_handler/test_schema.py =================================================================== --- tests/unittests/test_handler/test_schema.py +++ tests/unittests/test_handler/test_schema.py @@ -27,7 +27,13 @@ class GetSchemaTest(CiTestCase): """Every cloudconfig module with schema is listed in allOf keyword.""" schema = get_schema() self.assertItemsEqual( - ['cc_bootcmd', 'cc_ntp', 'cc_resizefs', 'cc_runcmd'], + [ + 'cc_bootcmd', + 'cc_ntp', + 'cc_resizefs', + 'cc_runcmd', + 'cc_zypper_add_repo' + ], [subschema['id'] for subschema in schema['allOf']]) self.assertEqual('cloud-config-schema', schema['id']) self.assertEqual( ++++++ zypp_add_repos.diff ++++++ --- /var/tmp/diff_new_pack.jeiljh/_old 2017-11-03 16:29:19.498438767 +0100 +++ /var/tmp/diff_new_pack.jeiljh/_new 2017-11-03 16:29:19.498438767 +0100 @@ -1,39 +1,110 @@ ---- cloudinit/config/cc_zypp_add_repo.py -+++ cloudinit/config/cc_zypp_add_repo.py 2016/11/23 13:10:49 -@@ -0,0 +1,112 @@ -+# vi: ts=4 expandtab -+# -+# Copyright (C) 2016 SUSE LLC. -+# This file is based on cc_yum_add_repo.py and was rewritten by -+# Thorsten Kukuk <[email protected]> for zypp repos. -+# -+# Original file was: -+# -+# Copyright (C) 2012 Yahoo! Inc. -+# -+# Author: Joshua Harlow <[email protected]> +Index: cloudinit/config/cc_zypp_add_repo.py +=================================================================== +--- /dev/null ++++ cloudinit/config/cc_zypper_add_repo.py +@@ -0,0 +1,220 @@ +# -+# 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. ++# Copyright (C) 2017 SUSE LLC. +# -+# 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 <http://www.gnu.org/licenses/>. ++# This file is part of cloud-init. See LICENSE file for license information. ++ ++"""zypper_add_repo: Add zyper repositories to the system""" + -+import os + +import configobj -+import six ++import os + ++ ++from cloudinit import log as logging +from cloudinit import util ++from cloudinit.config.schema import get_schema_doc ++from cloudinit.settings import PER_ALWAYS ++from six import string_types ++from textwrap import dedent + +distros = ['opensuse', 'sles'] + ++schema = { ++ 'id': 'cc_zypper_add_repo', ++ 'name': 'ZypperAddRepo', ++ 'title': 'Configure zypper behavior and add zypper repositories', ++ 'description': dedent("""\ ++ Configure zypper behavior by modifying /etc/zypp/zypp.conf. The ++ configuration writer is "dumb" and will simply append the provided ++ configuration options to the configuration file. Option settings ++ that may be duplicate will be resolved by the way the zypp.conf file ++ is parsed. The file is in INI format. ++ Add repositories to the system. No validation is performed on the ++ repository file entries, it is assumed the user is familiar with ++ the zypper repository file format."""), ++ 'distros': distros, ++ 'examples': [dedent("""\ ++ zypper: ++ repos: ++ - id: opensuse-oss ++ name: os-oss ++ baseurl: http://dl.opensuse.org/dist/leap/v/repo/oss/ ++ enabled: 1 ++ autorefresh: 1 ++ - id: opensuse-oss-update ++ name: os-oss-up ++ baseurl: http://dl.opensuse.org/dist/leap/v/update ++ # any setting per ++ # https://en.opensuse.org/openSUSE:Standards_RepoInfo ++ # enable and autorefresh are on by default ++ config: ++ reposdir: /etc/zypp/repos.dir ++ servicesdir: /etc/zypp/services.d ++ download.use_deltarpm: true ++ # any setting in /etc/zypp/zypp.conf ++ """)], ++ 'frequency': PER_ALWAYS, ++ 'type': 'object', ++ 'properties': { ++ 'zypper': { ++ 'type': 'object', ++ 'properties': { ++ 'repos': { ++ 'type': 'array', ++ 'items': { ++ 'type': 'object', ++ 'properties': { ++ 'id': { ++ 'type': 'string', ++ 'description': dedent("""\ ++ The unique id of the repo, used when ++ writing ++ /etc/zypp/repos.d/<id>.repo.""") ++ }, ++ 'baseurl': { ++ 'type': 'string', ++ 'format': 'uri', # built-in format type ++ 'description': 'The base repositoy URL' ++ } ++ }, ++ 'required': ['id', 'baseurl'], ++ 'additionalProperties': True ++ }, ++ 'minItems': 1 ++ }, ++ 'config': { ++ 'type': 'object', ++ 'description': dedent("""\ ++ Any supported zypo.conf key is written to ++ /etc/zypp/zypp.conf'""") ++ } ++ }, ++ 'required': [], ++ 'minProperties': 1, # Either config or repo must be provided ++ 'additionalProperties': False, # only repos and config allowed ++ } ++ } ++} ++ ++__doc__ = get_schema_doc(schema) # Supplement python help() ++ ++LOG = logging.getLogger(__name__) ++ + +def _canonicalize_id(repo_id): + repo_id = repo_id.replace(" ", "_") @@ -41,12 +112,12 @@ + + +def _format_repo_value(val): -+ if isinstance(val, (bool)): ++ if isinstance(val, bool): + # zypp prefers 1/0 -+ return str(int(val)) ++ return 1 if val else 0 + if isinstance(val, (list, tuple)): + return "\n ".join([_format_repo_value(v) for v in val]) -+ if not isinstance(val, six.string_types): ++ if not isinstance(val, string_types): + return str(val) + return val + @@ -63,58 +134,99 @@ + return "\n".join(lines) + + -+def handle(name, cfg, _cloud, log, _args): -+ repos = cfg.get('zypp_repos') ++def _write_repos(repos, repo_base_path): ++ """Write the user-provided repo definition files ++ @param repos: A list of repo dictionary objects provided by the user's ++ cloud config. ++ @param repo_base_path: The directory path to which repo definitions are ++ written. ++ """ ++ + if not repos: -+ log.debug(("Skipping module named %s," -+ " no 'zypp_repos' configuration found"), name) + return -+ repo_base_path = util.get_cfg_option_str(cfg, 'zypp_repo_dir', -+ '/etc/zypp/repos.d/') -+ repo_locations = {} -+ repo_configs = {} -+ for (repo_id, repo_config) in repos.items(): ++ valid_repos = {} ++ for index, user_repo_config in enumerate(repos): ++ # Skip on absent required keys ++ missing_keys = set(['id', 'baseurl']).difference(set(user_repo_config)) ++ if missing_keys: ++ LOG.warning( ++ "Repo config at index %d is missing required config keys: %s", ++ index, ",".join(missing_keys)) ++ continue ++ repo_id = user_repo_config.get('id') + canon_repo_id = _canonicalize_id(repo_id) + repo_fn_pth = os.path.join(repo_base_path, "%s.repo" % (canon_repo_id)) + if os.path.exists(repo_fn_pth): -+ log.info("Skipping repo %s, file %s already exists!", ++ LOG.info("Skipping repo %s, file %s already exists!", + repo_id, repo_fn_pth) + continue -+ elif repo_id in repo_locations: -+ log.info("Skipping repo %s, file %s already pending!", ++ elif repo_id in valid_repos: ++ LOG.info("Skipping repo %s, file %s already pending!", + repo_id, repo_fn_pth) + continue -+ if not repo_config: -+ repo_config = {} -+ # Do some basic sanity checks/cleaning -+ n_repo_config = {} -+ for (k, v) in repo_config.items(): -+ k = k.lower().strip().replace("-", "_") -+ if k: -+ n_repo_config[k] = v -+ repo_config = n_repo_config -+ missing_required = 0 -+ for req_field in ['baseurl']: -+ if req_field not in repo_config: -+ log.warn(("Repository %s does not contain a %s" -+ " configuration 'required' entry"), -+ repo_id, req_field) -+ missing_required += 1 ++ ++ # Do some basic key formatting ++ repo_config = dict( ++ (k.lower().strip().replace("-", "_"), v) ++ for k, v in user_repo_config.items() ++ if k and k != 'id') ++ ++ # Set defaults if not present + for field in ['enabled', 'autorefresh']: + if field not in repo_config: + repo_config[field] = '1' -+ if not missing_required: -+ repo_configs[repo_id] = repo_config -+ repo_locations[repo_id] = repo_fn_pth -+ else: -+ log.warn("Repository %s is missing %s required fields, skipping!", -+ repo_id, missing_required) -+ for (c_repo_id, path) in repo_locations.items(): -+ repo_blob = _format_repository_config(c_repo_id, -+ repo_configs.get(c_repo_id)) -+ util.write_file(path, repo_blob) ---- doc/examples/cloud-config-zypp-repo.txt -+++ doc/examples/cloud-config-zypp-repo.txt 2016/11/23 12:59:42 ++ ++ valid_repos[repo_id] = (repo_fn_pth, repo_config) ++ ++ for (repo_id, repo_data) in valid_repos.items(): ++ repo_blob = _format_repository_config(repo_id, repo_data[-1]) ++ util.write_file(repo_data[0], repo_blob) ++ ++ ++def _write_zypp_config(zypper_config): ++ """Write to the default zypp configuration file /etc/zypp/zypp.conf""" ++ if not zypper_config: ++ return ++ zypp_config = '/etc/zypp/zypp.conf' ++ zypp_conf_content = util.load_file(zypp_config) ++ new_settings = ['# Added via cloud.cfg'] ++ for setting, value in zypper_config.items(): ++ if setting == 'configdir': ++ msg = 'Changing the location of the zypper configuration is ' ++ msg += 'not supported, skipping "configdir" setting' ++ LOG.warning(msg) ++ continue ++ if value: ++ new_settings.append('%s=%s' % (setting, value)) ++ if len(new_settings) > 1: ++ new_config = zypp_conf_content + '\n'.join(new_settings) ++ else: ++ new_config = zypp_conf_content ++ util.write_file(zypp_config, new_config) ++ ++ ++def handle(name, cfg, _cloud, log, _args): ++ zypper_section = cfg.get('zypper') ++ if not zypper_section: ++ LOG.debug(("Skipping module named %s," ++ " no 'zypper' relevant configuration found"), name) ++ return ++ repos = zypper_section.get('repos') ++ if not repos: ++ LOG.debug(("Skipping module named %s," ++ " no 'repos' configuration found"), name) ++ return ++ zypper_config = zypper_section.get('config', {}) ++ repo_base_path = zypper_config.get('reposdir', '/etc/zypp/repos.d/') ++ ++ _write_zypp_config(zypper_config) ++ _write_repos(repos, repo_base_path) ++ ++# vi: ts=4 expandtab +Index: doc/examples/cloud-config-zypp-repo.txt +=================================================================== +--- /dev/null ++++ doc/examples/cloud-config-zypp-repo.txt @@ -0,0 +1,18 @@ +#cloud-config +# vim: syntax=yaml
