Skip to content

Commit 932c073

Browse files
committed
Openstack: Vendor data cleanup
For now, this vendor data handling is just added to openstack. However, in an effort to allow sanely handling of multi-part vendor-data that is namespaced, we add openstack.convert_vendordata_json . That basically takes whatever was loaded from vendordata and takes the 'cloud-init' key if it is a dict. This way the author can namespace cloud-init, basically telling it to ignore everything else.
1 parent 2755274 commit 932c073

File tree

4 files changed

+72
-13
lines changed

4 files changed

+72
-13
lines changed

cloudinit/sources/DataSourceConfigDrive.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,13 @@ def get_data(self):
126126
self.version = results['version']
127127
self.files.update(results.get('files', {}))
128128

129-
# If there is no vendordata, set vd to an empty dict instead of None
130-
vd = results.get('vendordata', {})
131-
# if vendordata includes 'cloud-init', then read that explicitly
132-
# for cloud-init (for namespacing).
133-
if 'cloud-init' in vd:
134-
self.vendordata_raw = vd['cloud-init']
129+
vd = results.get('vendordata')
130+
self.vendordata_pure = vd
131+
try:
132+
self.vendordata_raw = openstack.convert_vendordata_json(vd)
133+
except ValueError as e:
134+
LOG.warn("Invalid content in vendor-data: %s", e)
135+
self.vendordata_raw = None
135136

136137
return True
137138

cloudinit/sources/DataSourceOpenStack.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -140,13 +140,13 @@ def get_data(self):
140140
self.version = results['version']
141141
self.files.update(results.get('files', {}))
142142

143-
# if vendordata includes 'cloud-init', then read that explicitly
144-
# for cloud-init (for namespacing).
145143
vd = results.get('vendordata')
146-
if isinstance(vd, dict) and 'cloud-init' in vd:
147-
self.vendordata_raw = vd['cloud-init']
148-
else:
149-
self.vendordata_raw = vd
144+
self.vendordata_pure = vd
145+
try:
146+
self.vendordata_raw = openstack.convert_vendordata_json(vd)
147+
except ValueError as e:
148+
LOG.warn("Invalid content in vendor-data: %s", e)
149+
self.vendordata_raw = None
150150

151151
return True
152152

cloudinit/sources/helpers/openstack.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,3 +459,28 @@ def _read_ec2_metadata(self):
459459
return ec2_utils.get_instance_metadata(ssl_details=self.ssl_details,
460460
timeout=self.timeout,
461461
retries=self.retries)
462+
463+
464+
def convert_vendordata_json(data, recurse=True):
465+
""" data: a loaded json *object* (strings, arrays, dicts).
466+
return something suitable for cloudinit vendordata_raw.
467+
468+
if data is:
469+
None: return None
470+
string: return string
471+
list: return data
472+
the list is then processed in UserDataProcessor
473+
dict: return convert_vendordata_json(data.get('cloud-init'))
474+
"""
475+
if not data:
476+
return None
477+
if isinstance(data, (str, unicode, basestring)):
478+
return data
479+
if isinstance(data, list):
480+
return copy.deepcopy(data)
481+
if isinstance(data, dict):
482+
if recurse is True:
483+
return convert_vendordata_json(data.get('cloud-init'),
484+
recurse=False)
485+
raise ValueError("vendordata['cloud-init'] cannot be dict")
486+
raise ValueError("Unknown data type for vendordata: %s" % type(data))

tests/unittests/test_datasource/test_openstack.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import copy
2020
import json
2121
import re
22+
import unittest
2223

2324
from StringIO import StringIO
2425

@@ -256,7 +257,8 @@ def test_datasource(self):
256257
self.assertEquals(EC2_META, ds_os.ec2_metadata)
257258
self.assertEquals(USER_DATA, ds_os.userdata_raw)
258259
self.assertEquals(2, len(ds_os.files))
259-
self.assertEquals(VENDOR_DATA, ds_os.vendordata_raw)
260+
self.assertEquals(VENDOR_DATA, ds_os.vendordata_pure)
261+
self.assertEquals(ds_os.vendordata_raw, None)
260262

261263
@hp.activate
262264
def test_bad_datasource_meta(self):
@@ -314,3 +316,34 @@ def test_disabled_datasource(self):
314316
found = ds_os.get_data()
315317
self.assertFalse(found)
316318
self.assertIsNone(ds_os.version)
319+
320+
321+
class TestVendorDataLoading(unittest.TestCase):
322+
def cvj(self, data):
323+
return openstack.convert_vendordata_json(data)
324+
325+
def test_vd_load_none(self):
326+
# non-existant vendor-data should return none
327+
self.assertIsNone(self.cvj(None))
328+
329+
def test_vd_load_string(self):
330+
self.assertEqual(self.cvj("foobar"), "foobar")
331+
332+
def test_vd_load_list(self):
333+
data = [{'foo': 'bar'}, 'mystring', list(['another', 'list'])]
334+
self.assertEqual(self.cvj(data), data)
335+
336+
def test_vd_load_dict_no_ci(self):
337+
self.assertEqual(self.cvj({'foo': 'bar'}), None)
338+
339+
def test_vd_load_dict_ci_dict(self):
340+
self.assertRaises(ValueError, self.cvj,
341+
{'foo': 'bar', 'cloud-init': {'x': 1}})
342+
343+
def test_vd_load_dict_ci_string(self):
344+
data = {'foo': 'bar', 'cloud-init': 'VENDOR_DATA'}
345+
self.assertEqual(self.cvj(data), data['cloud-init'])
346+
347+
def test_vd_load_dict_ci_list(self):
348+
data = {'foo': 'bar', 'cloud-init': ['VD_1', 'VD_2']}
349+
self.assertEqual(self.cvj(data), data['cloud-init'])

0 commit comments

Comments
 (0)