Chad Smith has proposed merging ~chad.smith/cloud-init:ubuntu/artful into cloud-init:ubuntu/artful.
Commit message: Cherry pick fixes for IBMCloud upgrades from 17.1 -> 18.2 (artful-proposed) Addresses the following bugs: LP: #1766401 LP: #1767166 Requested reviews: cloud-init commiters (cloud-init-dev) Related bugs: Bug #1766401 in cloud-init: "full config file wiped after apt-upgrade issued" https://bugs.launchpad.net/cloud-init/+bug/1766401 Bug #1767166 in cloud-init: "IBMCloud datasource does not recognize provisioning in debug mode." https://bugs.launchpad.net/cloud-init/+bug/1767166 For more details, see: https://code.launchpad.net/~chad.smith/cloud-init/+git/cloud-init/+merge/344901 -- Your team cloud-init commiters is requested to review the proposed merge of ~chad.smith/cloud-init:ubuntu/artful into cloud-init:ubuntu/artful.
diff --git a/debian/changelog b/debian/changelog index 982d0c8..2399d6c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,12 @@ +cloud-init (18.2-4-g05926e48-0ubuntu1~17.10.2) artful-proposed; urgency=medium + + * cherry-pick 11172924: IBMCloud: Disable config-drive and nocloud + only if IBMCloud (LP: #1766401) + * cherry-pick 6ef92c98: IBMCloud: recognize provisioning environment + during debug (LP: #1767166) + + -- Chad Smith <[email protected]> Tue, 01 May 2018 09:56:23 -0600 + cloud-init (18.2-4-g05926e48-0ubuntu1~17.10.1) artful-proposed; urgency=medium * debian/new-upstream-snapshot: Remove script, now maintained elsewhere. diff --git a/debian/patches/cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if b/debian/patches/cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if new file mode 100644 index 0000000..18cfcff --- /dev/null +++ b/debian/patches/cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if @@ -0,0 +1,236 @@ +From 11172924a48a47a7231d19d9cefe628dfddda8bf Mon Sep 17 00:00:00 2001 +From: Scott Moser <[email protected]> +Date: Mon, 30 Apr 2018 13:21:51 -0600 +Subject: [PATCH] IBMCloud: Disable config-drive and nocloud only if IBMCloud + is enabled. + +Ubuntu images on IBMCloud for 16.04 have some seed data in +/var/lib/cloud/data/seed/nocloud-net. In order to have systems with +IBMCloud enabled, we modified ds-identify detection to skip that seed +if the system was on IBMCloud. That change did not consider the +fact that IBMCloud might not be in the datasource list. + +There was similar logic in the ConfigDrive datasource in ds-identify +and the datasource itself. + +Config drive is now updated to only check and avoid IBMCloud if IBMCloud +is enabled. The check in ds-identify for nocloud was dropped. If a +user provides a nocloud seed on IBMCloud, then that can be used. + +This means that systems running Xenial will continue to get their +old datasources. + +LP: #1766401 +--- + cloudinit/sources/DataSourceConfigDrive.py | 11 +++-- + tests/unittests/test_ds_identify.py | 77 +++++++++++++++++++++++++++--- + tools/ds-identify | 17 +++++-- + 3 files changed, 91 insertions(+), 14 deletions(-) + +Index: cloud-init/cloudinit/sources/DataSourceConfigDrive.py +=================================================================== +--- cloud-init.orig/cloudinit/sources/DataSourceConfigDrive.py ++++ cloud-init/cloudinit/sources/DataSourceConfigDrive.py +@@ -69,7 +69,8 @@ class DataSourceConfigDrive(openstack.So + util.logexc(LOG, "Failed reading config drive from %s", sdir) + + if not found: +- for dev in find_candidate_devs(): ++ dslist = self.sys_cfg.get('datasource_list') ++ for dev in find_candidate_devs(dslist=dslist): + try: + # Set mtype if freebsd and turn off sync + if dev.startswith("/dev/cd"): +@@ -211,7 +212,7 @@ def write_injected_files(files): + util.logexc(LOG, "Failed writing file: %s", filename) + + +-def find_candidate_devs(probe_optical=True): ++def find_candidate_devs(probe_optical=True, dslist=None): + """Return a list of devices that may contain the config drive. + + The returned list is sorted by search order where the first item has +@@ -227,6 +228,9 @@ def find_candidate_devs(probe_optical=Tr + * either vfat or iso9660 formated + * labeled with 'config-2' or 'CONFIG-2' + """ ++ if dslist is None: ++ dslist = [] ++ + # query optical drive to get it in blkid cache for 2.6 kernels + if probe_optical: + for device in OPTICAL_DEVICES: +@@ -257,7 +261,8 @@ def find_candidate_devs(probe_optical=Tr + devices = [d for d in candidates + if d in by_label or not util.is_partition(d)] + +- if devices: ++ LOG.debug("devices=%s dslist=%s", devices, dslist) ++ if devices and "IBMCloud" in dslist: + # IBMCloud uses config-2 label, but limited to a single UUID. + ibm_platform, ibm_path = get_ibm_platform() + if ibm_path in devices: +Index: cloud-init/tests/unittests/test_ds_identify.py +=================================================================== +--- cloud-init.orig/tests/unittests/test_ds_identify.py ++++ cloud-init/tests/unittests/test_ds_identify.py +@@ -178,17 +178,18 @@ class TestDsIdentify(CiTestCase): + data, RC_FOUND, dslist=[data.get('ds'), DS_NONE]) + + def _check_via_dict(self, data, rc, dslist=None, **kwargs): +- found_rc, out, err, cfg, files = self._call_via_dict(data, **kwargs) ++ ret = self._call_via_dict(data, **kwargs) + good = False + try: +- self.assertEqual(rc, found_rc) ++ self.assertEqual(rc, ret.rc) + if dslist is not None: +- self.assertEqual(dslist, cfg['datasource_list']) ++ self.assertEqual(dslist, ret.cfg['datasource_list']) + good = True + finally: + if not good: +- _print_run_output(rc, out, err, cfg, files) +- return rc, out, err, cfg, files ++ _print_run_output(ret.rc, ret.stdout, ret.stderr, ret.cfg, ++ ret.files) ++ return ret + + def test_wb_print_variables(self): + """_print_info reports an array of discovered variables to stderr.""" +@@ -237,13 +238,40 @@ class TestDsIdentify(CiTestCase): + def test_config_drive(self): + """ConfigDrive datasource has a disk with LABEL=config-2.""" + self._test_ds_found('ConfigDrive') +- return + + def test_config_drive_upper(self): + """ConfigDrive datasource has a disk with LABEL=CONFIG-2.""" + self._test_ds_found('ConfigDriveUpper') + return + ++ def test_config_drive_seed(self): ++ """Config Drive seed directory.""" ++ self._test_ds_found('ConfigDrive-seed') ++ ++ def test_config_drive_interacts_with_ibmcloud_config_disk(self): ++ """Verify ConfigDrive interaction with IBMCloud. ++ ++ If ConfigDrive is enabled and not IBMCloud, then ConfigDrive ++ should claim the ibmcloud 'config-2' disk. ++ If IBMCloud is enabled, then ConfigDrive should skip.""" ++ data = copy.deepcopy(VALID_CFG['IBMCloud-config-2']) ++ files = data.get('files', {}) ++ if not files: ++ data['files'] = files ++ cfgpath = 'etc/cloud/cloud.cfg.d/99_networklayer_common.cfg' ++ ++ # with list including IBMCloud, config drive should be not found. ++ files[cfgpath] = 'datasource_list: [ ConfigDrive, IBMCloud ]\n' ++ ret = self._check_via_dict(data, shell_true) ++ self.assertEqual( ++ ret.cfg.get('datasource_list'), ['IBMCloud', 'None']) ++ ++ # But if IBMCloud is not enabled, config drive should claim this. ++ files[cfgpath] = 'datasource_list: [ ConfigDrive, NoCloud ]\n' ++ ret = self._check_via_dict(data, shell_true) ++ self.assertEqual( ++ ret.cfg.get('datasource_list'), ['ConfigDrive', 'None']) ++ + def test_ibmcloud_template_userdata_in_provisioning(self): + """Template provisioned with user-data during provisioning stage. + +@@ -295,6 +323,37 @@ class TestDsIdentify(CiTestCase): + self._check_via_dict( + data, rc=RC_FOUND, dslist=['ConfigDrive', DS_NONE]) + ++ def test_ibmcloud_with_nocloud_seed(self): ++ """NoCloud seed should be preferred over IBMCloud. ++ ++ A nocloud seed should be preferred over IBMCloud even if enabled. ++ Ubuntu 16.04 images have <vlc>/seed/nocloud-net. LP: #1766401.""" ++ data = copy.deepcopy(VALID_CFG['IBMCloud-config-2']) ++ files = data.get('files', {}) ++ if not files: ++ data['files'] = files ++ files.update(VALID_CFG['NoCloud-seed']['files']) ++ ret = self._check_via_dict(data, shell_true) ++ self.assertEqual( ++ ['NoCloud', 'IBMCloud', 'None'], ++ ret.cfg.get('datasource_list')) ++ ++ def test_ibmcloud_with_configdrive_seed(self): ++ """ConfigDrive seed should be preferred over IBMCloud. ++ ++ A ConfigDrive seed should be preferred over IBMCloud even if enabled. ++ Ubuntu 16.04 images have a fstab entry that mounts the ++ METADATA disk into <vlc>/seed/config_drive. LP: ##1766401.""" ++ data = copy.deepcopy(VALID_CFG['IBMCloud-config-2']) ++ files = data.get('files', {}) ++ if not files: ++ data['files'] = files ++ files.update(VALID_CFG['ConfigDrive-seed']['files']) ++ ret = self._check_via_dict(data, shell_true) ++ self.assertEqual( ++ ['ConfigDrive', 'IBMCloud', 'None'], ++ ret.cfg.get('datasource_list')) ++ + def test_policy_disabled(self): + """A Builtin policy of 'disabled' should return not found. + +@@ -631,6 +690,12 @@ VALID_CFG = { + }, + ], + }, ++ 'ConfigDrive-seed': { ++ 'ds': 'ConfigDrive', ++ 'files': { ++ os.path.join(P_SEED_DIR, 'config_drive', 'openstack', ++ 'latest', 'meta_data.json'): 'md\n'}, ++ }, + 'Hetzner': { + 'ds': 'Hetzner', + 'files': {P_SYS_VENDOR: 'Hetzner\n'}, +Index: cloud-init/tools/ds-identify +=================================================================== +--- cloud-init.orig/tools/ds-identify ++++ cloud-init/tools/ds-identify +@@ -600,7 +600,6 @@ dscheck_NoCloud() { + *\ ds=nocloud*) return ${DS_FOUND};; + esac + +- is_ibm_cloud && return ${DS_NOT_FOUND} + for d in nocloud nocloud-net; do + check_seed_dir "$d" meta-data user-data && return ${DS_FOUND} + check_writable_seed_dir "$d" meta-data user-data && return ${DS_FOUND} +@@ -611,11 +610,12 @@ dscheck_NoCloud() { + return ${DS_NOT_FOUND} + } + ++is_ds_enabled() { ++ local name="$1" pad=" ${DI_DSLIST} " ++ [ "${pad#* $name }" != "${pad}" ] ++} ++ + check_configdrive_v2() { +- is_ibm_cloud && return ${DS_NOT_FOUND} +- if has_fs_with_label CONFIG-2 config-2; then +- return ${DS_FOUND} +- fi + # look in /config-drive <vlc>/seed/config_drive for a directory + # openstack/YYYY-MM-DD format with a file meta_data.json + local d="" +@@ -630,6 +630,13 @@ check_configdrive_v2() { + debug 1 "config drive seeded directory had only 'latest'" + return ${DS_FOUND} + fi ++ ++ is_ds_enabled "IBMCloud" ++ debug 1 "is_ds_enabled returned $?: $DI_DSLIST" ++ is_ds_enabled "IBMCloud" && is_ibm_cloud && return ${DS_NOT_FOUND} ++ if has_fs_with_label CONFIG-2 config-2; then ++ return ${DS_FOUND} ++ fi + return ${DS_NOT_FOUND} + } + diff --git a/debian/patches/cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during b/debian/patches/cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during new file mode 100644 index 0000000..962fe06 --- /dev/null +++ b/debian/patches/cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during @@ -0,0 +1,399 @@ +From 6ef92c98c3d2b127b05d6708337efc8a81e00071 Mon Sep 17 00:00:00 2001 +From: Scott Moser <[email protected]> +Date: Thu, 26 Apr 2018 16:24:24 -0500 +Subject: [PATCH] IBMCloud: recognize provisioning environment during debug + boots. + +When images are deployed from template in a production environment +the artifacts of the provisioning stage (provisioningConfiguration.cfg) +that cloud-init referenced are cleaned up. However, when provisioned +in "debug" mode (internal to IBM) the artifacts are left. + +This changes the 'is_ibm_provisioning' implementations in both +ds-identify and in the IBM datasource to identify the provisioning +stage more correctly. The change is to consider provisioning only +if the provisioing file existed and there was no log file or +the log file was older than this boot. + +LP: #1767166 +--- + cloudinit/sources/DataSourceIBMCloud.py | 42 +++++++++----- + cloudinit/tests/helpers.py | 13 ++++- + tests/unittests/test_datasource/test_ibmcloud.py | 50 ++++++++++++++++ + tests/unittests/test_ds_identify.py | 72 +++++++++++++++++++++--- + tools/ds-identify | 21 ++++++- + 5 files changed, 175 insertions(+), 23 deletions(-) + +Index: cloud-init/cloudinit/sources/DataSourceIBMCloud.py +=================================================================== +--- cloud-init.orig/cloudinit/sources/DataSourceIBMCloud.py ++++ cloud-init/cloudinit/sources/DataSourceIBMCloud.py +@@ -8,17 +8,11 @@ There are 2 different api exposed launch + * template: This is the legacy method of launching instances. + When booting from an image template, the system boots first into + a "provisioning" mode. There, host <-> guest mechanisms are utilized +- to execute code in the guest and provision it. ++ to execute code in the guest and configure it. The configuration ++ includes configuring the system network and possibly installing ++ packages and other software stack. + +- Cloud-init will disable itself when it detects that it is in the +- provisioning mode. It detects this by the presence of +- a file '/root/provisioningConfiguration.cfg'. +- +- When provided with user-data, the "first boot" will contain a +- ConfigDrive-like disk labeled with 'METADATA'. If there is no user-data +- provided, then there is no data-source. +- +- Cloud-init never does any network configuration in this mode. ++ After the provisioning is finished, the system reboots. + + * os_code: Essentially "launch by OS Code" (Operating System Code). + This is a more modern approach. There is no specific "provisioning" boot. +@@ -138,8 +132,30 @@ def _is_xen(): + return os.path.exists("/proc/xen") + + +-def _is_ibm_provisioning(): +- return os.path.exists("/root/provisioningConfiguration.cfg") ++def _is_ibm_provisioning( ++ prov_cfg="/root/provisioningConfiguration.cfg", ++ inst_log="/root/swinstall.log", ++ boot_ref="/proc/1/environ"): ++ """Return boolean indicating if this boot is ibm provisioning boot.""" ++ if os.path.exists(prov_cfg): ++ msg = "config '%s' exists." % prov_cfg ++ result = True ++ if os.path.exists(inst_log): ++ if os.path.exists(boot_ref): ++ result = (os.stat(inst_log).st_mtime > ++ os.stat(boot_ref).st_mtime) ++ msg += (" log '%s' from %s boot." % ++ (inst_log, "current" if result else "previous")) ++ else: ++ msg += (" log '%s' existed, but no reference file '%s'." % ++ (inst_log, boot_ref)) ++ result = False ++ else: ++ msg += " log '%s' did not exist." % inst_log ++ else: ++ result, msg = (False, "config '%s' did not exist." % prov_cfg) ++ LOG.debug("ibm_provisioning=%s: %s", result, msg) ++ return result + + + def get_ibm_platform(): +@@ -189,7 +205,7 @@ def get_ibm_platform(): + else: + return (Platforms.TEMPLATE_LIVE_METADATA, metadata_path) + elif _is_ibm_provisioning(): +- return (Platforms.TEMPLATE_PROVISIONING_NODATA, None) ++ return (Platforms.TEMPLATE_PROVISIONING_NODATA, None) + return not_found + + +Index: cloud-init/cloudinit/tests/helpers.py +=================================================================== +--- cloud-init.orig/cloudinit/tests/helpers.py ++++ cloud-init/cloudinit/tests/helpers.py +@@ -8,6 +8,7 @@ import os + import shutil + import sys + import tempfile ++import time + import unittest + + import mock +@@ -285,7 +286,8 @@ class FilesystemMockingTestCase(Resource + os.path: [('isfile', 1), ('exists', 1), + ('islink', 1), ('isdir', 1), ('lexists', 1)], + os: [('listdir', 1), ('mkdir', 1), +- ('lstat', 1), ('symlink', 2)] ++ ('lstat', 1), ('symlink', 2), ++ ('stat', 1)] + } + + if hasattr(os, 'scandir'): +@@ -354,6 +356,15 @@ def populate_dir(path, files): + return ret + + ++def populate_dir_with_ts(path, data): ++ """data is {'file': ('contents', mtime)}. mtime relative to now.""" ++ populate_dir(path, dict((k, v[0]) for k, v in data.items())) ++ btime = time.time() ++ for fpath, (_contents, mtime) in data.items(): ++ ts = btime + mtime if mtime else btime ++ os.utime(os.path.sep.join((path, fpath)), (ts, ts)) ++ ++ + def dir2dict(startdir, prefix=None): + flist = {} + if prefix is None: +Index: cloud-init/tests/unittests/test_datasource/test_ibmcloud.py +=================================================================== +--- cloud-init.orig/tests/unittests/test_datasource/test_ibmcloud.py ++++ cloud-init/tests/unittests/test_datasource/test_ibmcloud.py +@@ -259,4 +259,54 @@ class TestReadMD(test_helpers.CiTestCase + ret['metadata']) + + ++class TestIsIBMProvisioning(test_helpers.FilesystemMockingTestCase): ++ """Test the _is_ibm_provisioning method.""" ++ inst_log = "/root/swinstall.log" ++ prov_cfg = "/root/provisioningConfiguration.cfg" ++ boot_ref = "/proc/1/environ" ++ with_logs = True ++ ++ def _call_with_root(self, rootd): ++ self.reRoot(rootd) ++ return ibm._is_ibm_provisioning() ++ ++ def test_no_config(self): ++ """No provisioning config means not provisioning.""" ++ self.assertFalse(self._call_with_root(self.tmp_dir())) ++ ++ def test_config_only(self): ++ """A provisioning config without a log means provisioning.""" ++ rootd = self.tmp_dir() ++ test_helpers.populate_dir(rootd, {self.prov_cfg: "key=value"}) ++ self.assertTrue(self._call_with_root(rootd)) ++ ++ def test_config_with_old_log(self): ++ """A config with a log from previous boot is not provisioning.""" ++ rootd = self.tmp_dir() ++ data = {self.prov_cfg: ("key=value\nkey2=val2\n", -10), ++ self.inst_log: ("log data\n", -30), ++ self.boot_ref: ("PWD=/", 0)} ++ test_helpers.populate_dir_with_ts(rootd, data) ++ self.assertFalse(self._call_with_root(rootd=rootd)) ++ self.assertIn("from previous boot", self.logs.getvalue()) ++ ++ def test_config_with_new_log(self): ++ """A config with a log from this boot is provisioning.""" ++ rootd = self.tmp_dir() ++ data = {self.prov_cfg: ("key=value\nkey2=val2\n", -10), ++ self.inst_log: ("log data\n", 30), ++ self.boot_ref: ("PWD=/", 0)} ++ test_helpers.populate_dir_with_ts(rootd, data) ++ self.assertTrue(self._call_with_root(rootd=rootd)) ++ self.assertIn("from current boot", self.logs.getvalue()) ++ ++ def test_config_and_log_no_reference(self): ++ """If the config and log existed, but no reference, assume not.""" ++ rootd = self.tmp_dir() ++ test_helpers.populate_dir( ++ rootd, {self.prov_cfg: "key=value", self.inst_log: "log data\n"}) ++ self.assertFalse(self._call_with_root(rootd=rootd)) ++ self.assertIn("no reference file", self.logs.getvalue()) ++ ++ + # vi: ts=4 expandtab +Index: cloud-init/tests/unittests/test_ds_identify.py +=================================================================== +--- cloud-init.orig/tests/unittests/test_ds_identify.py ++++ cloud-init/tests/unittests/test_ds_identify.py +@@ -1,5 +1,6 @@ + # This file is part of cloud-init. See LICENSE file for license information. + ++from collections import namedtuple + import copy + import os + from uuid import uuid4 +@@ -7,7 +8,7 @@ from uuid import uuid4 + from cloudinit import safeyaml + from cloudinit import util + from cloudinit.tests.helpers import ( +- CiTestCase, dir2dict, populate_dir) ++ CiTestCase, dir2dict, populate_dir, populate_dir_with_ts) + + from cloudinit.sources import DataSourceIBMCloud as dsibm + +@@ -66,7 +67,6 @@ P_SYS_VENDOR = "sys/class/dmi/id/sys_ven + P_SEED_DIR = "var/lib/cloud/seed" + P_DSID_CFG = "etc/cloud/ds-identify.cfg" + +-IBM_PROVISIONING_CHECK_PATH = "/root/provisioningConfiguration.cfg" + IBM_CONFIG_UUID = "9796-932E" + + MOCK_VIRT_IS_KVM = {'name': 'detect_virt', 'RET': 'kvm', 'ret': 0} +@@ -74,11 +74,17 @@ MOCK_VIRT_IS_VMWARE = {'name': 'detect_v + MOCK_VIRT_IS_XEN = {'name': 'detect_virt', 'RET': 'xen', 'ret': 0} + MOCK_UNAME_IS_PPC64 = {'name': 'uname', 'out': UNAME_PPC64EL, 'ret': 0} + ++shell_true = 0 ++shell_false = 1 + +-class TestDsIdentify(CiTestCase): ++CallReturn = namedtuple('CallReturn', ++ ['rc', 'stdout', 'stderr', 'cfg', 'files']) ++ ++ ++class DsIdentifyBase(CiTestCase): + dsid_path = os.path.realpath('tools/ds-identify') + +- def call(self, rootd=None, mocks=None, args=None, files=None, ++ def call(self, rootd=None, mocks=None, func="main", args=None, files=None, + policy_dmi=DI_DEFAULT_POLICY, + policy_no_dmi=DI_DEFAULT_POLICY_NO_DMI, + ec2_strict_id=DI_EC2_STRICT_ID_DEFAULT): +@@ -135,7 +141,7 @@ class TestDsIdentify(CiTestCase): + mocklines.append(write_mock(d)) + + endlines = [ +- 'main %s' % ' '.join(['"%s"' % s for s in args]) ++ func + ' ' + ' '.join(['"%s"' % s for s in args]) + ] + + with open(wrap, "w") as fp: +@@ -159,7 +165,7 @@ class TestDsIdentify(CiTestCase): + cfg = {"_INVALID_YAML": contents, + "_EXCEPTION": str(e)} + +- return rc, out, err, cfg, dir2dict(rootd) ++ return CallReturn(rc, out, err, cfg, dir2dict(rootd)) + + def _call_via_dict(self, data, rootd=None, **kwargs): + # return output of self.call with a dict input like VALID_CFG[item] +@@ -191,6 +197,8 @@ class TestDsIdentify(CiTestCase): + ret.files) + return ret + ++ ++class TestDsIdentify(DsIdentifyBase): + def test_wb_print_variables(self): + """_print_info reports an array of discovered variables to stderr.""" + data = VALID_CFG['Azure-dmi-detection'] +@@ -278,7 +286,10 @@ class TestDsIdentify(CiTestCase): + Template provisioning with user-data has METADATA disk, + datasource should return not found.""" + data = copy.deepcopy(VALID_CFG['IBMCloud-metadata']) +- data['files'] = {IBM_PROVISIONING_CHECK_PATH: 'xxx'} ++ # change the 'is_ibm_provisioning' mock to return 1 (false) ++ isprov_m = [m for m in data['mocks'] ++ if m["name"] == "is_ibm_provisioning"][0] ++ isprov_m['ret'] = shell_true + return self._check_via_dict(data, RC_NOT_FOUND) + + def test_ibmcloud_template_userdata(self): +@@ -293,7 +304,8 @@ class TestDsIdentify(CiTestCase): + + no disks attached. Datasource should return not found.""" + data = copy.deepcopy(VALID_CFG['IBMCloud-nodisks']) +- data['files'] = {IBM_PROVISIONING_CHECK_PATH: 'xxx'} ++ data['mocks'].append( ++ {'name': 'is_ibm_provisioning', 'ret': shell_true}) + return self._check_via_dict(data, RC_NOT_FOUND) + + def test_ibmcloud_template_no_userdata(self): +@@ -505,6 +517,47 @@ class TestDsIdentify(CiTestCase): + self._test_ds_found('Hetzner') + + ++class TestIsIBMProvisioning(DsIdentifyBase): ++ """Test the is_ibm_provisioning method in ds-identify.""" ++ ++ inst_log = "/root/swinstall.log" ++ prov_cfg = "/root/provisioningConfiguration.cfg" ++ boot_ref = "/proc/1/environ" ++ funcname = "is_ibm_provisioning" ++ ++ def test_no_config(self): ++ """No provisioning config means not provisioning.""" ++ ret = self.call(files={}, func=self.funcname) ++ self.assertEqual(shell_false, ret.rc) ++ ++ def test_config_only(self): ++ """A provisioning config without a log means provisioning.""" ++ ret = self.call(files={self.prov_cfg: "key=value"}, func=self.funcname) ++ self.assertEqual(shell_true, ret.rc) ++ ++ def test_config_with_old_log(self): ++ """A config with a log from previous boot is not provisioning.""" ++ rootd = self.tmp_dir() ++ data = {self.prov_cfg: ("key=value\nkey2=val2\n", -10), ++ self.inst_log: ("log data\n", -30), ++ self.boot_ref: ("PWD=/", 0)} ++ populate_dir_with_ts(rootd, data) ++ ret = self.call(rootd=rootd, func=self.funcname) ++ self.assertEqual(shell_false, ret.rc) ++ self.assertIn("from previous boot", ret.stderr) ++ ++ def test_config_with_new_log(self): ++ """A config with a log from this boot is provisioning.""" ++ rootd = self.tmp_dir() ++ data = {self.prov_cfg: ("key=value\nkey2=val2\n", -10), ++ self.inst_log: ("log data\n", 30), ++ self.boot_ref: ("PWD=/", 0)} ++ populate_dir_with_ts(rootd, data) ++ ret = self.call(rootd=rootd, func=self.funcname) ++ self.assertEqual(shell_true, ret.rc) ++ self.assertIn("from current boot", ret.stderr) ++ ++ + def blkid_out(disks=None): + """Convert a list of disk dictionaries into blkid content.""" + if disks is None: +@@ -704,6 +757,7 @@ VALID_CFG = { + 'ds': 'IBMCloud', + 'mocks': [ + MOCK_VIRT_IS_XEN, ++ {'name': 'is_ibm_provisioning', 'ret': shell_false}, + {'name': 'blkid', 'ret': 0, + 'out': blkid_out( + [{'DEVNAME': 'xvda1', 'TYPE': 'vfat', 'PARTUUID': uuid4()}, +@@ -717,6 +771,7 @@ VALID_CFG = { + 'ds': 'IBMCloud', + 'mocks': [ + MOCK_VIRT_IS_XEN, ++ {'name': 'is_ibm_provisioning', 'ret': shell_false}, + {'name': 'blkid', 'ret': 0, + 'out': blkid_out( + [{'DEVNAME': 'xvda1', 'TYPE': 'ext3', 'PARTUUID': uuid4(), +@@ -734,6 +789,7 @@ VALID_CFG = { + 'ds': 'IBMCloud', + 'mocks': [ + MOCK_VIRT_IS_XEN, ++ {'name': 'is_ibm_provisioning', 'ret': shell_false}, + {'name': 'blkid', 'ret': 0, + 'out': blkid_out( + [{'DEVNAME': 'xvda1', 'TYPE': 'vfat', 'PARTUUID': uuid4()}, +Index: cloud-init/tools/ds-identify +=================================================================== +--- cloud-init.orig/tools/ds-identify ++++ cloud-init/tools/ds-identify +@@ -125,6 +125,7 @@ DI_ON_NOTFOUND="" + DI_EC2_STRICT_ID_DEFAULT="true" + + _IS_IBM_CLOUD="" ++_IS_IBM_PROVISIONING="" + + error() { + set -- "ERROR:" "$@"; +@@ -1013,7 +1014,25 @@ dscheck_Hetzner() { + } + + is_ibm_provisioning() { +- [ -f "${PATH_ROOT}/root/provisioningConfiguration.cfg" ] ++ local pcfg="${PATH_ROOT}/root/provisioningConfiguration.cfg" ++ local logf="${PATH_ROOT}/root/swinstall.log" ++ local is_prov=false msg="config '$pcfg' did not exist." ++ if [ -f "$pcfg" ]; then ++ msg="config '$pcfg' exists." ++ is_prov=true ++ if [ -f "$logf" ]; then ++ if [ "$logf" -nt "$PATH_PROC_1_ENVIRON" ]; then ++ msg="$msg log '$logf' from current boot." ++ else ++ is_prov=false ++ msg="$msg log '$logf' from previous boot." ++ fi ++ else ++ msg="$msg log '$logf' did not exist." ++ fi ++ fi ++ debug 2 "ibm_provisioning=$is_prov: $msg" ++ [ "$is_prov" = "true" ] + } + + is_ibm_cloud() { diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 0000000..2f90183 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1,2 @@ +cpick-11172924-IBMCloud-Disable-config-drive-and-nocloud-only-if +cpick-6ef92c98-IBMCloud-recognize-provisioning-environment-during
_______________________________________________ Mailing list: https://launchpad.net/~cloud-init-dev Post to : [email protected] Unsubscribe : https://launchpad.net/~cloud-init-dev More help : https://help.launchpad.net/ListHelp

