diff -Nru cloud-init-0.7.7~bzr1155/debian/changelog cloud-init-0.7.7~bzr1156/debian/changelog --- cloud-init-0.7.7~bzr1155/debian/changelog 2015-11-10 10:35:43.000000000 -0700 +++ cloud-init-0.7.7~bzr1156/debian/changelog 2015-11-18 10:48:27.000000000 -0700 @@ -1,3 +1,11 @@ +cloud-init (0.7.7~bzr1156-0ubuntu1) xenial; urgency=medium + + * New upstream snapshot. + * d/cloud-init.preinst: migrate Azure instance ID from old ID to stable + ID (LP: #1506187). + + -- Ben Howard Tue, 17 Nov 2015 11:59:49 -0700 + cloud-init (0.7.7~bzr1155-0ubuntu1) xenial; urgency=medium * New upstream snapshot. diff -Nru cloud-init-0.7.7~bzr1155/debian/cloud-init.preinst cloud-init-0.7.7~bzr1156/debian/cloud-init.preinst --- cloud-init-0.7.7~bzr1155/debian/cloud-init.preinst 2015-11-05 09:59:04.000000000 -0700 +++ cloud-init-0.7.7~bzr1156/debian/cloud-init.preinst 2015-11-18 10:35:28.000000000 -0700 @@ -105,6 +105,73 @@ return 0 } +azure_apply_new_instance_id_1506187() { + # With LP: #1506187, the Azure instance ID detection method was changed + # to use the DMI data. In order to prevent existing instances from thinking + # they are new instances, the instance ID needs to be updated here. + + if grep DataSourceAzure /var/lib/cloud/instance/datasource > /dev/null 2>&1; then + + product_id_f="/sys/devices/virtual/dmi/id/product_uuid" + instance_id_f="/var/lib/cloud/data/instance-id" + + if [ ! -e "${product_id_f}" -o ! -e "${instance_id_f}" ]; then + return 0 + fi + + # Get the current instance ID's (new and old) + new_instance_id="$(cat ${product_id_f})" + old_instance_id="$(cat ${instance_id_f})" + + if [ "${new_instance_id}" = "${old_instance_id}" ]; then + # this may have been applied for a prior version, i.e. upgrading + # from 14.04 to 16.04 + return 0 + + elif [ -z "${new_instance_id}" -o -z "${old_instance_id}" ]; then + cat < /var/lib/cloud/data/instance-id + + # Remove the symlink for the instance + rm /var/lib/cloud/instance + + # Rename the old instance id to the new one + mv /var/lib/cloud/instances/${old_instance_id} \ + /var/lib/cloud/instances/${new_instance_id} + + # Link the old id to the new one, just incase + ln -s /var/lib/cloud/instances/${new_instance_id} \ + /var/lib/cloud/instances/${old_instance_id} + + # Make the active instance the new id + ln -s /var/lib/cloud/instances/${new_instance_id} \ + /var/lib/cloud/instance + fi +fi +} + case "$1" in install|upgrade) # removing obsolete conffiles from the 'ec2-init' package @@ -130,7 +197,7 @@ for f in ${old_confs}; do rm_conffile cloud-init "/etc/init/${f}.conf" done - fi + fi if dpkg --compare-versions "$2" le "0.5.11-0ubuntu1"; then # rename the config entries in sem/ so they're not run again @@ -159,7 +226,11 @@ if [ -e /var/lib/cloud/instance/sem/user-scripts ]; then ln -sf user-scripts /var/lib/cloud/instance/sem/config-scripts-user fi - + + # 0.7.7-bzr1556 introduced new instance ID source for Azure + if dpkg --compare-versions "$2" le "0.7.7-bzr1556-0ubuntu1"; then + azure_apply_new_instance_id_1506187 + fi d=/etc/cloud/ if [ -f "$d/distro.cfg" ] && [ ! -f "$d/cloud.cfg.d/90_dpkg.cfg" ]; then diff -Nru cloud-init-0.7.7~bzr1155/debian/patches/lp-1506187-azure-new-instance-id.patch cloud-init-0.7.7~bzr1156/debian/patches/lp-1506187-azure-new-instance-id.patch --- cloud-init-0.7.7~bzr1155/debian/patches/lp-1506187-azure-new-instance-id.patch 1969-12-31 17:00:00.000000000 -0700 +++ cloud-init-0.7.7~bzr1156/debian/patches/lp-1506187-azure-new-instance-id.patch 2015-11-17 17:23:19.000000000 -0700 @@ -0,0 +1,390 @@ +Description: AZURE: use new instance IDs + Azure is now using DMI Data for instance IDs. +Author: Ben Howard +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1506187 + +--- +The information above should follow the Patch Tagging Guidelines, please +checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here +are templates for supplementary fields that you might want to add: + +Origin: , +Bug: +Bug-Debian: https://bugs.debian.org/ +Bug-Ubuntu: https://launchpad.net/bugs/ +Forwarded: +Reviewed-By: +Last-Update: + +--- cloud-init-0.7.7~bzr1155.orig/cloudinit/sources/DataSourceAzure.py ++++ cloud-init-0.7.7~bzr1155/cloudinit/sources/DataSourceAzure.py +@@ -31,8 +31,7 @@ from cloudinit import log as logging + from cloudinit.settings import PER_ALWAYS + from cloudinit import sources + from cloudinit import util +-from cloudinit.sources.helpers.azure import ( +- get_metadata_from_fabric, iid_from_shared_config_content) ++from cloudinit.sources.helpers.azure import get_metadata_from_fabric + + LOG = logging.getLogger(__name__) + +@@ -41,7 +40,6 @@ DEFAULT_METADATA = {"instance-id": "iid- + AGENT_START = ['service', 'walinuxagent', 'start'] + BOUNCE_COMMAND = ['sh', '-xc', + "i=$interface; x=0; ifdown $i || x=$?; ifup $i || x=$?; exit $x"] +-DATA_DIR_CLEAN_LIST = ['SharedConfig.xml'] + + BUILTIN_DS_CONFIG = { + 'agent_command': AGENT_START, +@@ -144,8 +142,6 @@ class DataSourceAzureNet(sources.DataSou + self.ds_cfg['agent_command']) + + ddir = self.ds_cfg['data_dir'] +- shcfgxml = os.path.join(ddir, "SharedConfig.xml") +- wait_for = [shcfgxml] + + fp_files = [] + key_value = None +@@ -160,19 +156,11 @@ class DataSourceAzureNet(sources.DataSou + + missing = util.log_time(logfunc=LOG.debug, msg="waiting for files", + func=wait_for_files, +- args=(wait_for + fp_files,)) ++ args=(fp_files,)) + if len(missing): + LOG.warn("Did not find files, but going on: %s", missing) + + metadata = {} +- if shcfgxml in missing: +- LOG.warn("SharedConfig.xml missing, using static instance-id") +- else: +- try: +- metadata['instance-id'] = iid_from_shared_config(shcfgxml) +- except ValueError as e: +- LOG.warn("failed to get instance id in %s: %s", shcfgxml, e) +- + metadata['public-keys'] = key_value or pubkeys_from_crt_files(fp_files) + return metadata + +@@ -229,21 +217,6 @@ class DataSourceAzureNet(sources.DataSou + user_ds_cfg = util.get_cfg_by_path(self.cfg, DS_CFG_PATH, {}) + self.ds_cfg = util.mergemanydict([user_ds_cfg, self.ds_cfg]) + +- if found != ddir: +- cached_ovfenv = util.load_file( +- os.path.join(ddir, 'ovf-env.xml'), quiet=True, decode=False) +- if cached_ovfenv != files['ovf-env.xml']: +- # source was not walinux-agent's datadir, so we have to clean +- # up so 'wait_for_files' doesn't return early due to stale data +- cleaned = [] +- for f in [os.path.join(ddir, f) for f in DATA_DIR_CLEAN_LIST]: +- if os.path.exists(f): +- util.del_file(f) +- cleaned.append(f) +- if cleaned: +- LOG.info("removed stale file(s) in '%s': %s", +- ddir, str(cleaned)) +- + # walinux agent writes files world readable, but expects + # the directory to be protected. + write_files(ddir, files, dirmode=0o700) +@@ -259,6 +232,7 @@ class DataSourceAzureNet(sources.DataSou + " on Azure.", exc_info=True) + return False + ++ self.metadata['instance-id'] = util.read_dmi_data('system-uuid') + self.metadata.update(fabric_data) + + found_ephemeral = find_fabric_formatted_ephemeral_disk() +@@ -649,12 +623,6 @@ def load_azure_ds_dir(source_dir): + return (md, ud, cfg, {'ovf-env.xml': contents}) + + +-def iid_from_shared_config(path): +- with open(path, "rb") as fp: +- content = fp.read() +- return iid_from_shared_config_content(content) +- +- + class BrokenAzureDataSource(Exception): + pass + +--- cloud-init-0.7.7~bzr1155.orig/cloudinit/sources/helpers/azure.py ++++ cloud-init-0.7.7~bzr1155/cloudinit/sources/helpers/azure.py +@@ -79,12 +79,6 @@ class GoalState(object): + './Container/RoleInstanceList/RoleInstance/InstanceId') + + @property +- def shared_config_xml(self): +- url = self._text_from_xpath('./Container/RoleInstanceList/RoleInstance' +- '/Configuration/SharedConfig') +- return self.http_client.get(url).contents +- +- @property + def certificates_xml(self): + if self._certificates_xml is None: + url = self._text_from_xpath( +@@ -172,19 +166,6 @@ class OpenSSLManager(object): + return keys + + +-def iid_from_shared_config_content(content): +- """ +- find INSTANCE_ID in: +- +- +- +- +- """ +- root = ElementTree.fromstring(content) +- depnode = root.find('Deployment') +- return depnode.get('name') +- +- + class WALinuxAgentShim(object): + + REPORT_READY_XML_TEMPLATE = '\n'.join([ +@@ -263,8 +244,6 @@ class WALinuxAgentShim(object): + public_keys = self.openssl_manager.parse_certificates( + goal_state.certificates_xml) + data = { +- 'instance-id': iid_from_shared_config_content( +- goal_state.shared_config_xml), + 'public-keys': public_keys, + } + self._report_ready(goal_state, http_client) +--- cloud-init-0.7.7~bzr1155.orig/tests/unittests/test_datasource/test_azure.py ++++ cloud-init-0.7.7~bzr1155/tests/unittests/test_datasource/test_azure.py +@@ -115,10 +115,6 @@ class TestAzureDataSource(TestCase): + data['pubkey_files'] = flist + return ["pubkey_from: %s" % f for f in flist] + +- def _iid_from_shared_config(path): +- data['iid_from_shared_cfg'] = path +- return 'i-my-azure-id' +- + if data.get('ovfcontent') is not None: + populate_dir(os.path.join(self.paths.seed_dir, "azure"), + {'ovf-env.xml': data['ovfcontent']}) +@@ -127,20 +123,22 @@ class TestAzureDataSource(TestCase): + mod.BUILTIN_DS_CONFIG['data_dir'] = self.waagent_d + + self.get_metadata_from_fabric = mock.MagicMock(return_value={ +- 'instance-id': 'i-my-azure-id', + 'public-keys': [], + }) + ++ self.instance_id = 'test-instance-id' ++ + self.apply_patches([ + (mod, 'list_possible_azure_ds_devs', dsdevs), + (mod, 'invoke_agent', _invoke_agent), + (mod, 'wait_for_files', _wait_for_files), + (mod, 'pubkeys_from_crt_files', _pubkeys_from_crt_files), +- (mod, 'iid_from_shared_config', _iid_from_shared_config), + (mod, 'perform_hostname_bounce', mock.MagicMock()), + (mod, 'get_hostname', mock.MagicMock()), + (mod, 'set_hostname', mock.MagicMock()), + (mod, 'get_metadata_from_fabric', self.get_metadata_from_fabric), ++ (mod.util, 'read_dmi_data', mock.MagicMock( ++ return_value=self.instance_id)), + ]) + + dsrc = mod.DataSourceAzureNet( +@@ -193,7 +191,6 @@ class TestAzureDataSource(TestCase): + self.assertEqual(dsrc.metadata['local-hostname'], odata['HostName']) + self.assertTrue(os.path.isfile( + os.path.join(self.waagent_d, 'ovf-env.xml'))) +- self.assertEqual(dsrc.metadata['instance-id'], 'i-my-azure-id') + + def test_waagent_d_has_0700_perms(self): + # we expect /var/lib/waagent to be created 0700 +@@ -345,7 +342,6 @@ class TestAzureDataSource(TestCase): + for mypk in mypklist: + self.assertIn(mypk['value'], dsrc.metadata['public-keys']) + +- + def test_default_ephemeral(self): + # make sure the ephemeral device works + odata = {} +@@ -434,54 +430,6 @@ class TestAzureDataSource(TestCase): + dsrc = self._get_ds({'ovfcontent': xml}) + dsrc.get_data() + +- def test_existing_ovf_same(self): +- # waagent/SharedConfig left alone if found ovf-env.xml same as cached +- odata = {'UserData': b64e("SOMEUSERDATA")} +- data = {'ovfcontent': construct_valid_ovf_env(data=odata)} +- +- populate_dir(self.waagent_d, +- {'ovf-env.xml': data['ovfcontent'], +- 'otherfile': 'otherfile-content', +- 'SharedConfig.xml': 'mysharedconfig'}) +- +- dsrc = self._get_ds(data) +- ret = dsrc.get_data() +- self.assertTrue(ret) +- self.assertTrue(os.path.exists( +- os.path.join(self.waagent_d, 'ovf-env.xml'))) +- self.assertTrue(os.path.exists( +- os.path.join(self.waagent_d, 'otherfile'))) +- self.assertTrue(os.path.exists( +- os.path.join(self.waagent_d, 'SharedConfig.xml'))) +- +- def test_existing_ovf_diff(self): +- # waagent/SharedConfig must be removed if ovfenv is found elsewhere +- +- # 'get_data' should remove SharedConfig.xml in /var/lib/waagent +- # if ovf-env.xml differs. +- cached_ovfenv = construct_valid_ovf_env( +- {'userdata': b64e("FOO_USERDATA")}) +- new_ovfenv = construct_valid_ovf_env( +- {'userdata': b64e("NEW_USERDATA")}) +- +- populate_dir(self.waagent_d, +- {'ovf-env.xml': cached_ovfenv, +- 'SharedConfig.xml': "mysharedconfigxml", +- 'otherfile': 'otherfilecontent'}) +- +- dsrc = self._get_ds({'ovfcontent': new_ovfenv}) +- ret = dsrc.get_data() +- self.assertTrue(ret) +- self.assertEqual(dsrc.userdata_raw, b"NEW_USERDATA") +- self.assertTrue(os.path.exists( +- os.path.join(self.waagent_d, 'otherfile'))) +- self.assertFalse(os.path.exists( +- os.path.join(self.waagent_d, 'SharedConfig.xml'))) +- self.assertTrue(os.path.exists( +- os.path.join(self.waagent_d, 'ovf-env.xml'))) +- new_xml = load_file(os.path.join(self.waagent_d, 'ovf-env.xml')) +- self.xml_equals(new_ovfenv, new_xml) +- + def test_exception_fetching_fabric_data_doesnt_propagate(self): + ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()}) + ds.ds_cfg['agent_command'] = '__builtin__' +@@ -496,6 +444,17 @@ class TestAzureDataSource(TestCase): + self.assertTrue(ret) + self.assertEqual('value', ds.metadata['test']) + ++ def test_instance_id_from_dmidecode_used(self): ++ ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()}) ++ ds.get_data() ++ self.assertEqual(self.instance_id, ds.metadata['instance-id']) ++ ++ def test_instance_id_from_dmidecode_used_for_builtin(self): ++ ds = self._get_ds({'ovfcontent': construct_valid_ovf_env()}) ++ ds.ds_cfg['agent_command'] = '__builtin__' ++ ds.get_data() ++ self.assertEqual(self.instance_id, ds.metadata['instance-id']) ++ + + class TestAzureBounce(TestCase): + +@@ -505,9 +464,6 @@ class TestAzureBounce(TestCase): + self.patches.enter_context( + mock.patch.object(DataSourceAzure, 'wait_for_files')) + self.patches.enter_context( +- mock.patch.object(DataSourceAzure, 'iid_from_shared_config', +- mock.MagicMock(return_value='i-my-azure-id'))) +- self.patches.enter_context( + mock.patch.object(DataSourceAzure, 'list_possible_azure_ds_devs', + mock.MagicMock(return_value=[]))) + self.patches.enter_context( +@@ -521,6 +477,9 @@ class TestAzureBounce(TestCase): + self.patches.enter_context( + mock.patch.object(DataSourceAzure, 'get_metadata_from_fabric', + mock.MagicMock(return_value={}))) ++ self.patches.enter_context( ++ mock.patch.object(DataSourceAzure.util, 'read_dmi_data', ++ mock.MagicMock(return_value='test-instance-id'))) + + def setUp(self): + super(TestAzureBounce, self).setUp() +--- cloud-init-0.7.7~bzr1155.orig/tests/unittests/test_datasource/test_azure_helper.py ++++ cloud-init-0.7.7~bzr1155/tests/unittests/test_datasource/test_azure_helper.py +@@ -40,7 +40,7 @@ GOAL_STATE_TEMPLATE = """\ + + http://100.86.192.70:80/...hostingEnvironmentConfig... + +- {shared_config_url} ++ http://100.86.192.70:80/..SharedConfig.. + + http://100.86.192.70:80/...extensionsConfig... + +@@ -55,21 +55,6 @@ GOAL_STATE_TEMPLATE = """\ + """ + + +-class TestReadAzureSharedConfig(unittest.TestCase): +- +- def test_valid_content(self): +- xml = """ +- +- +- +- +- +- +- """ +- ret = azure_helper.iid_from_shared_config_content(xml) +- self.assertEqual("MY_INSTANCE_ID", ret) +- +- + class TestFindEndpoint(TestCase): + + def setUp(self): +@@ -140,7 +125,6 @@ class TestGoalStateParsing(TestCase): + 'incarnation': 1, + 'container_id': 'MyContainerId', + 'instance_id': 'MyInstanceId', +- 'shared_config_url': 'MySharedConfigUrl', + 'certificates_url': 'MyCertificatesUrl', + } + +@@ -174,20 +158,9 @@ class TestGoalStateParsing(TestCase): + goal_state = self._get_goal_state(instance_id=instance_id) + self.assertEqual(instance_id, goal_state.instance_id) + +- def test_shared_config_xml_parsed_and_fetched_correctly(self): +- http_client = mock.MagicMock() +- shared_config_url = 'TestSharedConfigUrl' +- goal_state = self._get_goal_state( +- http_client=http_client, shared_config_url=shared_config_url) +- shared_config_xml = goal_state.shared_config_xml +- self.assertEqual(1, http_client.get.call_count) +- self.assertEqual(shared_config_url, http_client.get.call_args[0][0]) +- self.assertEqual(http_client.get.return_value.contents, +- shared_config_xml) +- + def test_certificates_xml_parsed_and_fetched_correctly(self): + http_client = mock.MagicMock() +- certificates_url = 'TestSharedConfigUrl' ++ certificates_url = 'TestCertificatesUrl' + goal_state = self._get_goal_state( + http_client=http_client, certificates_url=certificates_url) + certificates_xml = goal_state.certificates_xml +@@ -324,8 +297,6 @@ class TestWALinuxAgentShim(TestCase): + azure_helper.WALinuxAgentShim, 'find_endpoint')) + self.GoalState = patches.enter_context( + mock.patch.object(azure_helper, 'GoalState')) +- self.iid_from_shared_config_content = patches.enter_context( +- mock.patch.object(azure_helper, 'iid_from_shared_config_content')) + self.OpenSSLManager = patches.enter_context( + mock.patch.object(azure_helper, 'OpenSSLManager')) + patches.enter_context( +@@ -367,15 +338,6 @@ class TestWALinuxAgentShim(TestCase): + data = shim.register_with_azure_and_fetch_data() + self.assertEqual([], data['public-keys']) + +- def test_instance_id_returned_in_data(self): +- shim = azure_helper.WALinuxAgentShim() +- data = shim.register_with_azure_and_fetch_data() +- self.assertEqual( +- [mock.call(self.GoalState.return_value.shared_config_xml)], +- self.iid_from_shared_config_content.call_args_list) +- self.assertEqual(self.iid_from_shared_config_content.return_value, +- data['instance-id']) +- + def test_correct_url_used_for_report_ready(self): + self.find_endpoint.return_value = 'test_endpoint' + shim = azure_helper.WALinuxAgentShim() diff -Nru cloud-init-0.7.7~bzr1155/debian/patches/series cloud-init-0.7.7~bzr1156/debian/patches/series --- cloud-init-0.7.7~bzr1155/debian/patches/series 2015-11-05 09:59:04.000000000 -0700 +++ cloud-init-0.7.7~bzr1156/debian/patches/series 2015-11-17 17:23:19.000000000 -0700 @@ -0,0 +1 @@ +lp-1506187-azure-new-instance-id.patch