diff options
35 files changed, 328 insertions, 130 deletions
@@ -1,23 +1,36 @@ CWD=$(shell pwd) -PY_FILES=$(shell find cloudinit bin tests tools -name "*.py" -type f ) -PY_FILES+="bin/cloud-init" +PYVER ?= 3 +noseopts ?= -v YAML_FILES=$(shell find cloudinit bin tests tools -name "*.yaml" -type f ) YAML_FILES+=$(shell find doc/examples -name "cloud-config*.txt" -type f ) CHANGELOG_VERSION=$(shell $(CWD)/tools/read-version) CODE_VERSION=$(shell python -c "from cloudinit import version; print version.version_string()") -noseopts ?= -vv --nologcapture PIP_INSTALL := pip install +ifeq ($(PYVER),3) + pyflakes = pyflakes3 + unittests = unittest3 + yaml = yaml +else +ifeq ($(PYVER),2) + pyflakes = pyflakes + unittests = unittest +else + pyflakes = pyflakes pyflakes3 + unittests = unittest unittest3 +endif +endif + ifeq ($(distro),) distro = redhat endif all: check -check: check_version pep8 pyflakes pyflakes3 test +check: check_version pep8 $(pyflakes) test $(yaml) pep8: @$(CWD)/tools/run-pep8 @@ -30,6 +43,8 @@ pyflakes3: unittest: clean_pyc nosetests $(noseopts) tests/unittests + +unittest3: clean_pyc nosetests3 $(noseopts) tests/unittests pip-requirements: @@ -40,7 +55,7 @@ pip-test-requirements: @echo "Installing cloud-init test dependencies..." $(PIP_INSTALL) -r "$@.txt" -q -test: unittest +test: $(unittests) check_version: @if [ "$(CHANGELOG_VERSION)" != "$(CODE_VERSION)" ]; then \ @@ -51,9 +66,6 @@ check_version: clean_pyc: @find . -type f -name "*.pyc" -delete -2to3: - 2to3 $(PY_FILES) - clean: clean_pyc rm -rf /var/log/cloud-init.log /var/lib/cloud/ @@ -66,5 +78,5 @@ rpm: deb: ./packages/bddeb -.PHONY: test pyflakes pyflakes3 2to3 clean pep8 rpm deb yaml check_version -.PHONY: pip-test-requirements pip-requirements clean_pyc unittest +.PHONY: test pyflakes pyflakes3 clean pep8 rpm deb yaml check_version +.PHONY: pip-test-requirements pip-requirements clean_pyc unittest unittest3 diff --git a/cloudinit/config/cc_final_message.py b/cloudinit/config/cc_final_message.py index ad957e12..4a51476f 100644 --- a/cloudinit/config/cc_final_message.py +++ b/cloudinit/config/cc_final_message.py @@ -28,9 +28,9 @@ frequency = PER_ALWAYS # Jinja formated default message FINAL_MESSAGE_DEF = ( - "## template: jinja\n" - "Cloud-init v. {{version}} finished at {{timestamp}}." - " Datasource {{datasource}}. Up {{uptime}} seconds" + "## template: jinja\n" + "Cloud-init v. {{version}} finished at {{timestamp}}." + " Datasource {{datasource}}. Up {{uptime}} seconds" ) diff --git a/cloudinit/config/cc_grub_dpkg.py b/cloudinit/config/cc_grub_dpkg.py index acd3e60a..3c2d9985 100644 --- a/cloudinit/config/cc_grub_dpkg.py +++ b/cloudinit/config/cc_grub_dpkg.py @@ -37,9 +37,8 @@ def handle(name, cfg, _cloud, log, _args): return idevs = util.get_cfg_option_str(mycfg, "grub-pc/install_devices", None) - idevs_empty = util.get_cfg_option_str(mycfg, - "grub-pc/install_devices_empty", - None) + idevs_empty = util.get_cfg_option_str( + mycfg, "grub-pc/install_devices_empty", None) if ((os.path.exists("/dev/sda1") and not os.path.exists("/dev/sda")) or (os.path.exists("/dev/xvda1") and not os.path.exists("/dev/xvda"))): diff --git a/cloudinit/config/cc_landscape.py b/cloudinit/config/cc_landscape.py index 0b9d846e..68fcb27f 100644 --- a/cloudinit/config/cc_landscape.py +++ b/cloudinit/config/cc_landscape.py @@ -38,12 +38,12 @@ distros = ['ubuntu'] # defaults taken from stock client.conf in landscape-client 11.07.1.1-0ubuntu2 LSC_BUILTIN_CFG = { - 'client': { - 'log_level': "info", - 'url': "https://landscape.canonical.com/message-system", - 'ping_url': "http://landscape.canonical.com/ping", - 'data_path': "/var/lib/landscape/client", - } + 'client': { + 'log_level': "info", + 'url': "https://landscape.canonical.com/message-system", + 'ping_url': "http://landscape.canonical.com/ping", + 'data_path': "/var/lib/landscape/client", + } } diff --git a/cloudinit/helpers.py b/cloudinit/helpers.py index a6eb20fe..0cf982f3 100644 --- a/cloudinit/helpers.py +++ b/cloudinit/helpers.py @@ -336,19 +336,19 @@ class Paths(object): template_dir = path_cfgs.get('templates_dir', '/etc/cloud/templates/') self.template_tpl = os.path.join(template_dir, '%s.tmpl') self.lookups = { - "handlers": "handlers", - "scripts": "scripts", - "vendor_scripts": "scripts/vendor", - "sem": "sem", - "boothooks": "boothooks", - "userdata_raw": "user-data.txt", - "userdata": "user-data.txt.i", - "obj_pkl": "obj.pkl", - "cloud_config": "cloud-config.txt", - "vendor_cloud_config": "vendor-cloud-config.txt", - "data": "data", - "vendordata_raw": "vendor-data.txt", - "vendordata": "vendor-data.txt.i", + "handlers": "handlers", + "scripts": "scripts", + "vendor_scripts": "scripts/vendor", + "sem": "sem", + "boothooks": "boothooks", + "userdata_raw": "user-data.txt", + "userdata": "user-data.txt.i", + "obj_pkl": "obj.pkl", + "cloud_config": "cloud-config.txt", + "vendor_cloud_config": "vendor-cloud-config.txt", + "data": "data", + "vendordata_raw": "vendor-data.txt", + "vendordata": "vendor-data.txt.i", } # Set when a datasource becomes active self.datasource = ds diff --git a/cloudinit/sources/DataSourceAltCloud.py b/cloudinit/sources/DataSourceAltCloud.py index 60d58d6d..cd61df31 100644 --- a/cloudinit/sources/DataSourceAltCloud.py +++ b/cloudinit/sources/DataSourceAltCloud.py @@ -284,7 +284,7 @@ class DataSourceAltCloud(sources.DataSource): # In the future 'dsmode' like behavior can be added to offer user # the ability to run before networking. datasources = [ - (DataSourceAltCloud, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), + (DataSourceAltCloud, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), ] diff --git a/cloudinit/sources/DataSourceAzure.py b/cloudinit/sources/DataSourceAzure.py index b03ab895..2af0ad9b 100644 --- a/cloudinit/sources/DataSourceAzure.py +++ b/cloudinit/sources/DataSourceAzure.py @@ -636,7 +636,7 @@ class NonAzureDataSource(Exception): # Used to match classes to dependencies datasources = [ - (DataSourceAzureNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), + (DataSourceAzureNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), ] diff --git a/cloudinit/sources/DataSourceDigitalOcean.py b/cloudinit/sources/DataSourceDigitalOcean.py index 5d47564d..12e863d2 100644 --- a/cloudinit/sources/DataSourceDigitalOcean.py +++ b/cloudinit/sources/DataSourceDigitalOcean.py @@ -101,8 +101,8 @@ class DataSourceDigitalOcean(sources.DataSource): # Used to match classes to dependencies datasources = [ - (DataSourceDigitalOcean, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), - ] + (DataSourceDigitalOcean, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), +] # Return a list of data sources that match this set of dependencies diff --git a/cloudinit/sources/DataSourceEc2.py b/cloudinit/sources/DataSourceEc2.py index 6a897f7d..3ef2c6af 100644 --- a/cloudinit/sources/DataSourceEc2.py +++ b/cloudinit/sources/DataSourceEc2.py @@ -206,7 +206,7 @@ class DataSourceEc2(sources.DataSource): # Used to match classes to dependencies datasources = [ - (DataSourceEc2, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), + (DataSourceEc2, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), ] diff --git a/cloudinit/sources/DataSourceMAAS.py b/cloudinit/sources/DataSourceMAAS.py index f18c4cee..d828f078 100644 --- a/cloudinit/sources/DataSourceMAAS.py +++ b/cloudinit/sources/DataSourceMAAS.py @@ -254,7 +254,7 @@ class MAASSeedDirMalformed(Exception): # Used to match classes to dependencies datasources = [ - (DataSourceMAAS, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), + (DataSourceMAAS, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), ] diff --git a/cloudinit/sources/DataSourceNoCloud.py b/cloudinit/sources/DataSourceNoCloud.py index 4dffe6e6..4cad6877 100644 --- a/cloudinit/sources/DataSourceNoCloud.py +++ b/cloudinit/sources/DataSourceNoCloud.py @@ -263,8 +263,8 @@ class DataSourceNoCloudNet(DataSourceNoCloud): # Used to match classes to dependencies datasources = [ - (DataSourceNoCloud, (sources.DEP_FILESYSTEM, )), - (DataSourceNoCloudNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), + (DataSourceNoCloud, (sources.DEP_FILESYSTEM, )), + (DataSourceNoCloudNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), ] diff --git a/cloudinit/sources/DataSourceNone.py b/cloudinit/sources/DataSourceNone.py index 12a8a992..d1a62b2a 100644 --- a/cloudinit/sources/DataSourceNone.py +++ b/cloudinit/sources/DataSourceNone.py @@ -47,8 +47,8 @@ class DataSourceNone(sources.DataSource): # Used to match classes to dependencies datasources = [ - (DataSourceNone, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), - (DataSourceNone, []), + (DataSourceNone, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), + (DataSourceNone, []), ] diff --git a/cloudinit/sources/DataSourceOVF.py b/cloudinit/sources/DataSourceOVF.py index be467bad..8e97e51a 100644 --- a/cloudinit/sources/DataSourceOVF.py +++ b/cloudinit/sources/DataSourceOVF.py @@ -367,8 +367,8 @@ class XmlError(Exception): # Used to match classes to dependencies datasources = ( - (DataSourceOVF, (sources.DEP_FILESYSTEM, )), - (DataSourceOVFNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), + (DataSourceOVF, (sources.DEP_FILESYSTEM, )), + (DataSourceOVFNet, (sources.DEP_FILESYSTEM, sources.DEP_NETWORK)), ) diff --git a/cloudinit/sources/DataSourceOpenNebula.py b/cloudinit/sources/DataSourceOpenNebula.py index b26940d1..681f3a96 100644 --- a/cloudinit/sources/DataSourceOpenNebula.py +++ b/cloudinit/sources/DataSourceOpenNebula.py @@ -149,8 +149,8 @@ class BrokenContextDiskDir(Exception): class OpenNebulaNetwork(object): REG_DEV_MAC = re.compile( - r'^\d+: (eth\d+):.*?link\/ether (..:..:..:..:..:..) ?', - re.MULTILINE | re.DOTALL) + r'^\d+: (eth\d+):.*?link\/ether (..:..:..:..:..:..) ?', + re.MULTILINE | re.DOTALL) def __init__(self, ip, context): self.ip = ip diff --git a/cloudinit/sources/DataSourceSmartOS.py b/cloudinit/sources/DataSourceSmartOS.py index 139ee52c..5edab152 100644 --- a/cloudinit/sources/DataSourceSmartOS.py +++ b/cloudinit/sources/DataSourceSmartOS.py @@ -101,7 +101,7 @@ BUILTIN_CLOUD_CONFIG = { 'ephemeral0': {'table_type': 'mbr', 'layout': False, 'overwrite': False} - }, + }, 'fs_setup': [{'label': 'ephemeral0', 'filesystem': 'ext3', 'device': 'ephemeral0'}], diff --git a/doc/examples/cloud-config-mount-points.txt b/doc/examples/cloud-config-mount-points.txt index 3b45b47f..aa676c24 100644 --- a/doc/examples/cloud-config-mount-points.txt +++ b/doc/examples/cloud-config-mount-points.txt @@ -42,5 +42,5 @@ mount_default_fields: [ None, None, "auto", "defaults,nobootwait", "0", "2" ] # default is to not create any swap files, because 'size' is set to 0 swap: filename: /swap.img - size: "auto" or size in bytes + size: "auto" # or size in bytes maxsize: size in bytes diff --git a/doc/examples/cloud-config-rsyslog.txt b/doc/examples/cloud-config-rsyslog.txt index d54960e8..28ea1f16 100644 --- a/doc/examples/cloud-config-rsyslog.txt +++ b/doc/examples/cloud-config-rsyslog.txt @@ -17,7 +17,7 @@ rsyslog: - content: "*.* @@192.0.2.1:10514" filename: 01-example.conf - content: | - *.* @@syslogd.example.com + *.* @@syslogd.example.com config_dir: /etc/rsyslog.d config_filename: 20-cloud-config.conf service_reload_command: [your, syslog, reload, command] @@ -32,7 +32,7 @@ rsyslog: # - content: "*.* @@192.0.2.1:10514" # filename: 01-example.conf # - content: | -# *.* @@syslogd.example.com +# *.* @@syslogd.example.com # rsyslog_filename: 20-cloud-config.conf # rsyslog_dir: /etc/rsyslog.d diff --git a/packages/bddeb b/packages/bddeb index c4efe264..c141b1ab 100755 --- a/packages/bddeb +++ b/packages/bddeb @@ -1,5 +1,6 @@ #!/usr/bin/env python3 +import glob import os import shutil import sys @@ -105,11 +106,11 @@ def write_debian_folder(root, version, revno, pkgmap, util.abs_join(deb_dir, 'rules'), params={'python': python, 'pyver': pyver}) - # Just copy the following directly - for base_fn in ['dirs', 'copyright', 'compat']: - shutil.copy(util.abs_join(find_root(), - 'packages', 'debian', base_fn), - util.abs_join(deb_dir, base_fn)) + # Just copy any other files directly (including .in) + pdeb_d = util.abs_join(find_root(), 'packages', 'debian') + for f in [os.path.join(pdeb_d, f) for f in os.listdir(pdeb_d)]: + if os.path.isfile(f): + shutil.copy(f, util.abs_join(deb_dir, os.path.basename(f))) def main(): diff --git a/packages/debian/cloud-init.postinst b/packages/debian/cloud-init.postinst new file mode 100644 index 00000000..cdd0466d --- /dev/null +++ b/packages/debian/cloud-init.postinst @@ -0,0 +1,16 @@ +#!/bin/sh +cleanup_lp1552999() { + local oldver="$1" last_bad_ver="0.7.7~bzr1178" + dpkg --compare-versions "$oldver" le "$last_bad_ver" || return 0 + local edir="/etc/systemd/system/multi-user.target.wants" + rm -f "$edir/cloud-config.service" "$edir/cloud-final.service" \ + "$edir/cloud-init-local.service" "$edir/cloud-init.service" +} + + +#DEBHELPER# + +if [ "$1" = "configure" ]; then + oldver="$2" + cleanup_lp1552999 "$oldver" +fi diff --git a/packages/debian/cloud-init.preinst b/packages/debian/cloud-init.preinst new file mode 100644 index 00000000..3c2af06d --- /dev/null +++ b/packages/debian/cloud-init.preinst @@ -0,0 +1,20 @@ +#!/bin/sh +# vi: ts=4 expandtab + +cleanup_lp1552999() { + local oldver="$1" last_bad_ver="0.7.7~bzr1178" + dpkg --compare-versions "$oldver" le "$last_bad_ver" || return 0 + local hdir="/var/lib/systemd/deb-systemd-helper-enabled" + hdir="$hdir/multi-user.target.wants" + local edir="/etc/systemd/system/multi-user.target.wants" + rm -f "$hdir/cloud-config.service" "$hdir/cloud-final.service" \ + "$hdir/cloud-init-local.service" "$hdir/cloud-init.service" +} + + +if [ "$1" = "upgrade" ]; then + oldver="$2" + cleanup_lp1552999 "$oldver" +fi + +#DEBHELPER# diff --git a/packages/debian/control.in b/packages/debian/control.in index 5fe16e43..16713577 100644 --- a/packages/debian/control.in +++ b/packages/debian/control.in @@ -7,6 +7,7 @@ Build-Depends: debhelper (>= 9), dh-python, dh-systemd, iproute2, + pep8, pyflakes, ${python}, ${test_requires}, @@ -18,8 +19,8 @@ Package: cloud-init Architecture: all Depends: procps, ${python}, - ${requires}, ${misc:Depends}, + ${${python}:Depends} Recommends: eatmydata, sudo, software-properties-common, gdisk XB-Python-Version: ${python:Versions} Description: Init scripts for cloud instances diff --git a/packages/debian/rules.in b/packages/debian/rules.in index d6cd23ae..cf2dd405 100755 --- a/packages/debian/rules.in +++ b/packages/debian/rules.in @@ -1,9 +1,8 @@ ## template:basic #!/usr/bin/make -f - INIT_SYSTEM ?= upstart,systemd -PYVER ?= python${pyver} export PYBUILD_INSTALL_ARGS=--init-system=$(INIT_SYSTEM) +PYVER ?= python${pyver} %: dh $@ --with $(PYVER),systemd --buildsystem pybuild @@ -14,6 +13,11 @@ override_dh_install: cp tools/21-cloudinit.conf debian/cloud-init/etc/rsyslog.d/21-cloudinit.conf override_dh_auto_test: - # Because setup tools didn't copy data... - [ ! -d .pybuild/pythonX.Y_?.?/build/tests ] || cp -r tests/data .pybuild/pythonX.Y_?.?/build/tests - http_proxy= make check +ifeq (,$(findstring nocheck,$(DEB_BUILD_OPTIONS))) + http_proxy= make PYVER=${pyver} check +else + @echo check disabled by DEB_BUILD_OPTIONS=$(DEB_BUILD_OPTIONS) +endif + +override_dh_systemd_start: + dh_systemd_start --no-restart-on-upgrade --no-start @@ -45,39 +45,50 @@ def tiny_p(cmd, capture=True): stdout = None stderr = None sp = subprocess.Popen(cmd, stdout=stdout, - stderr=stderr, stdin=None, - universal_newlines=True) + stderr=stderr, stdin=None, + universal_newlines=True) (out, err) = sp.communicate() ret = sp.returncode if ret not in [0]: - raise RuntimeError("Failed running %s [rc=%s] (%s, %s)" - % (cmd, ret, out, err)) + raise RuntimeError("Failed running %s [rc=%s] (%s, %s)" % + (cmd, ret, out, err)) return (out, err) -def systemd_unitdir(): - cmd = ['pkg-config', '--variable=systemdsystemunitdir', 'systemd'] +def pkg_config_read(library, var): + fallbacks = { + 'systemd': { + 'systemdsystemunitdir': '/lib/systemd/system', + 'systemdsystemgeneratordir': '/lib/systemd/system-generators', + } + } + cmd = ['pkg-config', '--variable=%s' % var, library] try: (path, err) = tiny_p(cmd) except: - return '/lib/systemd/system' + return fallbacks[library][var] return str(path).strip() + INITSYS_FILES = { 'sysvinit': [f for f in glob('sysvinit/redhat/*') if is_f(f)], 'sysvinit_freebsd': [f for f in glob('sysvinit/freebsd/*') if is_f(f)], 'sysvinit_deb': [f for f in glob('sysvinit/debian/*') if is_f(f)], - 'systemd': [f for f in glob('systemd/*') if is_f(f)], + 'systemd': [f for f in (glob('systemd/*.service') + + glob('systemd/*.target')) if is_f(f)], + 'systemd.generators': [f for f in glob('systemd/*-generator') if is_f(f)], 'upstart': [f for f in glob('upstart/*') if is_f(f)], } INITSYS_ROOTS = { 'sysvinit': '/etc/rc.d/init.d', 'sysvinit_freebsd': '/usr/local/etc/rc.d', 'sysvinit_deb': '/etc/init.d', - 'systemd': systemd_unitdir(), + 'systemd': pkg_config_read('systemd', 'systemdsystemunitdir'), + 'systemd.generators': pkg_config_read('systemd', + 'systemdsystemgeneratordir'), 'upstart': '/etc/init/', } -INITSYS_TYPES = sorted(list(INITSYS_ROOTS.keys())) +INITSYS_TYPES = sorted([f.partition(".")[0] for f in INITSYS_ROOTS.keys()]) # Install everything in the right location and take care of Linux (default) and # FreeBSD systems. @@ -122,9 +133,8 @@ class InitsysInstallData(install): user_options = install.user_options + [ # This will magically show up in member variable 'init_sys' ('init-system=', None, - ('init system(s) to configure (%s) [default: None]') % - (", ".join(INITSYS_TYPES)) - ), + ('init system(s) to configure (%s) [default: None]' % + (", ".join(INITSYS_TYPES)))), ] def initialize_options(self): @@ -138,7 +148,8 @@ class InitsysInstallData(install): self.init_system = self.init_system.split(",") if len(self.init_system) == 0: - raise DistutilsArgError(("You must specify one of (%s) when" + raise DistutilsArgError( + ("You must specify one of (%s) when" " specifying init system(s)!") % (", ".join(INITSYS_TYPES))) bad = [f for f in self.init_system if f not in INITSYS_TYPES] @@ -147,8 +158,12 @@ class InitsysInstallData(install): "Invalid --init-system: %s" % (','.join(bad))) for system in self.init_system: - self.distribution.data_files.append( - (INITSYS_ROOTS[system], INITSYS_FILES[system])) + # add data files for anything that starts with '<system>.' + datakeys = [k for k in INITSYS_ROOTS + if k.partition(".")[0] == system] + for k in datakeys: + self.distribution.data_files.append( + (INITSYS_ROOTS[k], INITSYS_FILES[k])) # Force that command to reinitalize (with new file list) self.distribution.reinitialize_command('install_data', True) @@ -182,18 +197,18 @@ if sys.version_info < (3,): requirements.append('cheetah') -setuptools.setup(name='cloud-init', - version=get_version(), - description='EC2 initialisation magic', - author='Scott Moser', - author_email='scott.moser@canonical.com', - url='http://launchpad.net/cloud-init/', - packages=setuptools.find_packages(exclude=['tests']), - scripts=['bin/cloud-init', - 'tools/cloud-init-per', - ], - license='GPLv3', - data_files=data_files, - install_requires=requirements, - cmdclass=cmdclass, - ) +setuptools.setup( + name='cloud-init', + version=get_version(), + description='EC2 initialisation magic', + author='Scott Moser', + author_email='scott.moser@canonical.com', + url='http://launchpad.net/cloud-init/', + packages=setuptools.find_packages(exclude=['tests']), + scripts=['bin/cloud-init', + 'tools/cloud-init-per'], + license='GPLv3', + data_files=data_files, + install_requires=requirements, + cmdclass=cmdclass, + ) diff --git a/systemd/cloud-config.service b/systemd/cloud-config.service index f9f1996e..45d2a63b 100644 --- a/systemd/cloud-config.service +++ b/systemd/cloud-config.service @@ -13,4 +13,4 @@ TimeoutSec=0 StandardOutput=journal+console [Install] -WantedBy=multi-user.target +WantedBy=cloud-init.target diff --git a/systemd/cloud-final.service b/systemd/cloud-final.service index bcbdd36f..bfb08d4a 100644 --- a/systemd/cloud-final.service +++ b/systemd/cloud-final.service @@ -14,4 +14,4 @@ KillMode=process StandardOutput=journal+console [Install] -WantedBy=multi-user.target +WantedBy=cloud-init.target diff --git a/systemd/cloud-init-generator b/systemd/cloud-init-generator new file mode 100755 index 00000000..9d1e22f0 --- /dev/null +++ b/systemd/cloud-init-generator @@ -0,0 +1,128 @@ +#!/bin/sh +set -f + +LOG="" +DEBUG_LEVEL=1 +LOG_D="/run" +ENABLE="enabled" +DISABLE="disabled" +CLOUD_SYSTEM_TARGET="/lib/systemd/system/cloud-init.target" +CLOUD_TARGET_NAME="cloud-init.target" +# lxc sets 'container', but lets make that explicitly a global +CONTAINER="${container}" + +debug() { + local lvl="$1" + shift + [ "$lvl" -gt "$DEBUG_LEVEL" ] && return + if [ -z "$LOG" ]; then + local log="$LOG_D/${0##*/}.log" + { : > "$log"; } >/dev/null 2>&1 && LOG="$log" || + LOG="/dev/kmsg" + fi + echo "$@" >> "$LOG" +} + +etc_file() { + local pprefix="${1:-/etc/cloud/cloud-init.}" + _RET="unset" + [ -f "${pprefix}$ENABLE" ] && _RET="$ENABLE" && return 0 + [ -f "${pprefix}$DISABLE" ] && _RET="$DISABLE" && return 0 + return 0 +} + +read_proc_cmdline() { + if [ "$CONTAINER" = "lxc" ]; then + _RET_MSG="ignored: \$container=$CONTAINER" + _RET="" + return 0 + fi + + if systemd-detect-virt --container --quiet; then + _RET_MSG="ignored: detect-virt is container" + _RET="" + return 0 + fi + + _RET_MSG="/proc/cmdline" + read _RET < /proc/cmdline +} + +kernel_cmdline() { + local cmdline="" tok="" + if [ -n "${KERNEL_CMDLINE+x}" ]; then + # use KERNEL_CMDLINE if present in environment even if empty + cmdline=${KERNEL_CMDLINE} + debug 1 "kernel command line from env KERNEL_CMDLINE: $cmdline" + elif read_proc_cmdline; then + read_proc_cmdline && cmdline="$_RET" + debug 1 "kernel command line ($_RET_MSG): $cmdline" + fi + _RET="unset" + cmdline=" $cmdline " + tok=${cmdline##* cloud-init=} + [ "$tok" = "$cmdline" ] && _RET="unset" + tok=${tok%% *} + [ "$tok" = "$ENABLE" -o "$tok" = "$DISABLE" ] && _RET="$tok" + return 0 +} + +default() { + _RET="$ENABLE" +} + +main() { + local normal_d="$1" early_d="$2" late_d="$3" + local target_name="multi-user.target" gen_d="$early_d" + local link_path="$gen_d/${target_name}.wants/${CLOUD_TARGET_NAME}" + + debug 1 "$0 normal=$normal_d early=$early_d late=$late_d" + debug 2 "$0 $*" + + local search result="error" ret="" + for search in kernel_cmdline etc_file default; do + if $search; then + debug 1 "$search found $_RET" + [ "$_RET" = "$ENABLE" -o "$_RET" = "$DISABLE" ] && + result=$_RET && break + else + ret=$? + debug 0 "search $search returned $ret" + fi + done + + if [ "$result" = "$ENABLE" ]; then + if [ -e "$link_path" ]; then + debug 1 "already enabled: no change needed" + else + [ -d "${link_path%/*}" ] || mkdir -p "${link_path%/*}" || + debug 0 "failed to make dir $link_path" + if ln -snf "$CLOUD_SYSTEM_TARGET" "$link_path"; then + debug 1 "enabled via $link_path -> $CLOUD_SYSTEM_TARGET" + else + ret=$? + debug 0 "[$ret] enable failed:" \ + "ln $CLOUD_SYSTEM_TARGET $link_path" + fi + fi + elif [ "$result" = "$DISABLE" ]; then + if [ -f "$link_path" ]; then + if rm -f "$link_path"; then + debug 1 "disabled. removed existing $link_path" + else + ret=$? + debug 0 "[$ret] disable failed, remove $link_path" + fi + else + debug 1 "already disabled: no change needed [no $link_path]" + fi + else + debug 0 "unexpected result '$result'" + ret=3 + fi + return $ret +} + +main "$@" + +# vi: ts=4 expandtab diff --git a/systemd/cloud-init-local.service b/systemd/cloud-init-local.service index a31985c6..73aa46f6 100644 --- a/systemd/cloud-init-local.service +++ b/systemd/cloud-init-local.service @@ -13,4 +13,4 @@ TimeoutSec=0 StandardOutput=journal+console [Install] -WantedBy=multi-user.target +WantedBy=cloud-init.target diff --git a/systemd/cloud-init.service b/systemd/cloud-init.service index 48920283..1f656f7f 100644 --- a/systemd/cloud-init.service +++ b/systemd/cloud-init.service @@ -15,4 +15,4 @@ TimeoutSec=0 StandardOutput=journal+console [Install] -WantedBy=multi-user.target +WantedBy=cloud-init.target diff --git a/systemd/cloud-init.target b/systemd/cloud-init.target new file mode 100644 index 00000000..a63babb0 --- /dev/null +++ b/systemd/cloud-init.target @@ -0,0 +1,6 @@ +# cloud-init target is enabled by cloud-init-generator +# To disable it you can either: +# a.) boot with kernel cmdline of 'cloudinit=disabled' +# b.) touch a file /etc/cloud/cloud-init.disabled +[Unit] +Description=Cloud-init target diff --git a/tests/unittests/test_datasource/test_configdrive.py b/tests/unittests/test_datasource/test_configdrive.py index 3954ceb3..bfd787d1 100644 --- a/tests/unittests/test_datasource/test_configdrive.py +++ b/tests/unittests/test_datasource/test_configdrive.py @@ -61,16 +61,16 @@ CONTENT_0 = b'This is contents of /etc/foo.cfg\n' CONTENT_1 = b'# this is /etc/bar/bar.cfg\n' CFG_DRIVE_FILES_V2 = { - 'ec2/2009-04-04/meta-data.json': json.dumps(EC2_META), - 'ec2/2009-04-04/user-data': USER_DATA, - 'ec2/latest/meta-data.json': json.dumps(EC2_META), - 'ec2/latest/user-data': USER_DATA, - 'openstack/2012-08-10/meta_data.json': json.dumps(OSTACK_META), - 'openstack/2012-08-10/user_data': USER_DATA, - 'openstack/content/0000': CONTENT_0, - 'openstack/content/0001': CONTENT_1, - 'openstack/latest/meta_data.json': json.dumps(OSTACK_META), - 'openstack/latest/user_data': USER_DATA} + 'ec2/2009-04-04/meta-data.json': json.dumps(EC2_META), + 'ec2/2009-04-04/user-data': USER_DATA, + 'ec2/latest/meta-data.json': json.dumps(EC2_META), + 'ec2/latest/user-data': USER_DATA, + 'openstack/2012-08-10/meta_data.json': json.dumps(OSTACK_META), + 'openstack/2012-08-10/user_data': USER_DATA, + 'openstack/content/0000': CONTENT_0, + 'openstack/content/0001': CONTENT_1, + 'openstack/latest/meta_data.json': json.dumps(OSTACK_META), + 'openstack/latest/user_data': USER_DATA} class TestConfigDriveDataSource(TestCase): diff --git a/tests/unittests/test_datasource/test_opennebula.py b/tests/unittests/test_datasource/test_opennebula.py index 27adf21b..d796f030 100644 --- a/tests/unittests/test_datasource/test_opennebula.py +++ b/tests/unittests/test_datasource/test_opennebula.py @@ -20,7 +20,7 @@ TEST_VARS = { 'VAR7': 'single\\t', 'VAR8': 'double\\tword', 'VAR9': 'multi\\t\nline\n', - 'VAR10': '\\', # expect \ + 'VAR10': '\\', # expect '\' 'VAR11': '\'', # expect ' 'VAR12': '$', # expect $ } diff --git a/tests/unittests/test_datasource/test_smartos.py b/tests/unittests/test_datasource/test_smartos.py index 616e9f0e..5c49966a 100644 --- a/tests/unittests/test_datasource/test_smartos.py +++ b/tests/unittests/test_datasource/test_smartos.py @@ -462,8 +462,8 @@ class TestJoyentMetadataClient(helpers.FilesystemMockingTestCase): payloadstr = ' {0}'.format(self.response_parts['payload']) return ('V2 {length} {crc} {request_id} ' '{command}{payloadstr}\n'.format( - payloadstr=payloadstr, - **self.response_parts).encode('ascii')) + payloadstr=payloadstr, + **self.response_parts).encode('ascii')) self.metasource_data = None diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py index e4488e2a..4525f487 100644 --- a/tests/unittests/test_distros/test_user_data_normalize.py +++ b/tests/unittests/test_distros/test_user_data_normalize.py @@ -6,13 +6,13 @@ from ..helpers import TestCase bcfg = { - 'name': 'bob', - 'plain_text_passwd': 'ubuntu', - 'home': "/home/ubuntu", - 'shell': "/bin/bash", - 'lock_passwd': True, - 'gecos': "Ubuntu", - 'groups': ["foo"] + 'name': 'bob', + 'plain_text_passwd': 'ubuntu', + 'home': "/home/ubuntu", + 'shell': "/bin/bash", + 'lock_passwd': True, + 'gecos': "Ubuntu", + 'groups': ["foo"] } @@ -34,16 +34,11 @@ class TestUGNormalize(TestCase): def test_group_dict(self): distro = self._make_distro('ubuntu') g = {'groups': [ - { - 'ubuntu': ['foo', 'bar'], - 'bob': 'users', - }, - 'cloud-users', - { - 'bob': 'users2', - }, - ] - } + {'ubuntu': ['foo', 'bar'], + 'bob': 'users'}, + 'cloud-users', + {'bob': 'users2'} + ]} (_users, groups) = self._norm(g, distro) self.assertIn('ubuntu', groups) ub_members = groups['ubuntu'] diff --git a/tools/validate-yaml.py b/tools/validate-yaml.py index 6e164590..ed9037d9 100755 --- a/tools/validate-yaml.py +++ b/tools/validate-yaml.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """Try to read a YAML file and report any errors. """ @@ -13,6 +13,7 @@ basepython = python3 [testenv:pyflakes] basepython = python3 commands = {envpython} -m pyflakes {posargs:cloudinit/ tests/ tools/} + {envpython} -m pep8 {posargs:cloudinit/ tests/ tools/} # https://github.com/gabrielfalcao/HTTPretty/issues/223 setenv = |