diff options
-rw-r--r-- | cloudinit/sources/DataSourceSmartOS.py | 33 | ||||
-rw-r--r-- | tests/unittests/test_datasource/test_smartos.py | 76 |
2 files changed, 104 insertions, 5 deletions
diff --git a/cloudinit/sources/DataSourceSmartOS.py b/cloudinit/sources/DataSourceSmartOS.py index 140c7814..8db652ef 100644 --- a/cloudinit/sources/DataSourceSmartOS.py +++ b/cloudinit/sources/DataSourceSmartOS.py @@ -95,6 +95,34 @@ BUILTIN_CLOUD_CONFIG = { 'device': 'ephemeral0'}], } +BUILTIN_VENDOR_DATA = """ +#cloud-config: +write_files: + - encoding: b64 + owner: root:root + path: %(script_d)s/01_sdc-operator-script.sh + permissions: '0755' + content: | + """ + base64.b64encode("""#!/bin/sh +# This file is written as part of the default vendor data for +# SmartOS. This script looks for the SmartDC operator script +# and then executes it. It will be run each boot. +# +# This requires the Joyent Metadata client to be installed. +# On Ubuntu, it is provided via the joyent-mdata-client package +# Or you can get it via https://github.com/joyent/mdata-client + +my_path=$(dirname $0) +[ -x /usr/sbin/mdata-get ] || exit 1 + +/usr/sbin/mdata-get sdc:operator-script > \ + $my_path/operator-script || exit 0 + +[ -e $my_path/operator-script ] || exit 0 +chmod 0700 $my_path/operator-script +exec /run/sdc/operator-script +""") + # @datadictionary: this is legacy path for placing files from metadata # per the SmartOS location. It is not preferable, but is done for # legacy reasons @@ -186,6 +214,11 @@ class DataSourceSmartOS(sources.DataSource): if md['user-data']: ud = md['user-data'] + if not md['vendordata']: + md['vendordata'] = BUILTIN_VENDOR_DATA % { + 'script_d': self.user_script_d + } + self.metadata = util.mergemanydict([md, self.metadata]) self.userdata_raw = ud self.vendordata_raw = md['vendordata'] diff --git a/tests/unittests/test_datasource/test_smartos.py b/tests/unittests/test_datasource/test_smartos.py index ae427bb5..062b44ee 100644 --- a/tests/unittests/test_datasource/test_smartos.py +++ b/tests/unittests/test_datasource/test_smartos.py @@ -23,10 +23,12 @@ # import base64 -from cloudinit import helpers +from cloudinit import helpers as c_helpers +from cloudinit import stages +from cloudinit import util from cloudinit.sources import DataSourceSmartOS - -from mocker import MockerTestCase +from cloudinit.settings import (PER_INSTANCE) +from tests.unittests import helpers import os import os.path import re @@ -105,22 +107,38 @@ class MockSerial(object): yield '\n' -class TestSmartOSDataSource(MockerTestCase): +#class TestSmartOSDataSource(MockerTestCase): +class TestSmartOSDataSource(helpers.FilesystemMockingTestCase): def setUp(self): + helpers.FilesystemMockingTestCase.setUp(self) + # makeDir comes from MockerTestCase self.tmp = self.makeDir() self.legacy_user_d = self.makeDir() + # If you should want to watch the logs... + self._log = None + self._log_file = None + self._log_handler = None + # patch cloud_dir, so our 'seed_dir' is guaranteed empty - self.paths = helpers.Paths({'cloud_dir': self.tmp}) + self.paths = c_helpers.Paths({'cloud_dir': self.tmp}) self.unapply = [] super(TestSmartOSDataSource, self).setUp() def tearDown(self): + helpers.FilesystemMockingTestCase.tearDown(self) + if self._log_handler and self._log: + self._log.removeHandler(self._log_handler) apply_patches([i for i in reversed(self.unapply)]) super(TestSmartOSDataSource, self).tearDown() + def _patchIn(self, root): + self.restore() + self.patchOS(root) + self.patchUtils(root) + def apply_patches(self, patches): ret = apply_patches(patches) self.unapply += ret @@ -375,6 +393,54 @@ class TestSmartOSDataSource(MockerTestCase): self.assertFalse(found_new) + def test_vendor_data_not_default(self): + dsrc = self._get_ds(mockdata=MOCK_RETURNS) + ret = dsrc.get_data() + self.assertTrue(ret) + self.assertEquals(MOCK_RETURNS['sdc:operator-script'], + dsrc.metadata['vendordata']) + + def test_default_vendor_data(self): + my_returns = MOCK_RETURNS.copy() + def_op_script = my_returns['sdc:operator-script'] + del my_returns['sdc:operator-script'] + dsrc = self._get_ds(mockdata=my_returns) + ret = dsrc.get_data() + self.assertTrue(ret) + self.assertNotEquals(def_op_script, dsrc.metadata['vendordata']) + + self.replicateTestRoot('simple_ubuntu', self.tmp) + cfg = { + 'cloud_init_modules': ['write-files'], + } + cloud_cfg = util.yaml_dumps(cfg) + util.ensure_dir(os.path.join(self.tmp, 'etc', 'cloud')) + util.write_file(os.path.join(self.tmp, 'etc', + 'cloud', 'cloud.cfg'), cloud_cfg) + + self._patchIn(self.tmp) + + initer = stages.Init() + initer.read_cfg() + initer.datasource = dsrc + initer.initialize() + initer.fetch() + _iid = initer.instancify() + initer.update() + initer.cloudify().run('consume_data', + initer.consume_data, + args=[PER_INSTANCE], + freq=PER_INSTANCE) + mods = stages.Modules(initer) + (_which_ran, _failures) = mods.run_section('cloud_init_modules') + pb_script_fns = os.path.join(dsrc.paths.get_cpath('scripts'), + 'per-boot', '01_sdc-operator-script.sh') + self.assertTrue(os.path.isfile(pb_script_fns)) + self.assertTrue(os.access(pb_script_fns, os.X_OK)) + + with open(pb_script_fns, 'r') as fd: + self.assertIn("#!/bin/sh", fd.readlines()[0]) + def test_disable_iptables_flag(self): dsrc = self._get_ds(mockdata=MOCK_RETURNS) ret = dsrc.get_data() |