← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~wesley-wiedenmeier/cloud-init:revert-63501f44 into cloud-init:master

 

Wesley Wiedenmeier has proposed merging ~wesley-wiedenmeier/cloud-init:revert-63501f44 into cloud-init:master.

Requested reviews:
  cloud init development team (cloud-init-dev)

For more details, see:
https://code.launchpad.net/~wesley-wiedenmeier/cloud-init/+git/cloud-init/+merge/313665

Revert 63501f44 which regressed behavior of stages.fetch_base_config() and added unittests for stages.fetch_base_config()
-- 
Your team cloud init development team is requested to review the proposed merge of ~wesley-wiedenmeier/cloud-init:revert-63501f44 into cloud-init:master.
diff --git a/cloudinit/stages.py b/cloudinit/stages.py
index 86a1378..40e826a 100644
--- a/cloudinit/stages.py
+++ b/cloudinit/stages.py
@@ -847,16 +847,16 @@ class Modules(object):
 def fetch_base_config():
     base_cfgs = []
     default_cfg = util.get_builtin_cfg()
-
-    # Anything in your conf.d location??
-    # or the 'default' cloud.cfg location???
-    base_cfgs.append(util.read_conf_with_confd(CLOUD_CONFIG))
+    kern_contents = util.read_cc_from_cmdline()
 
     # Kernel/cmdline parameters override system config
-    kern_contents = util.read_cc_from_cmdline()
     if kern_contents:
         base_cfgs.append(util.load_yaml(kern_contents, default={}))
 
+    # Anything in your conf.d location??
+    # or the 'default' cloud.cfg location???
+    base_cfgs.append(util.read_conf_with_confd(CLOUD_CONFIG))
+
     # And finally the default gets to play
     if default_cfg:
         base_cfgs.append(default_cfg)
diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py
index a2355a7..14a7ef3 100644
--- a/tests/unittests/helpers.py
+++ b/tests/unittests/helpers.py
@@ -300,6 +300,34 @@ def dir2dict(startdir, prefix=None):
     return flist
 
 
+def wrap_and_call(prefix, mocks, func, *args, **kwargs):
+    """
+    call func(args, **kwargs) with mocks applied, then unapplies mocks
+    nicer to read than repeating dectorators on each function
+
+    prefix: prefix for mock names (e.g. 'cloudinit.stages.util')
+    mocks: dictionary of names (under 'prefix') to mock and either
+        a return value or a dictionary to pass to the mock.patch call
+    func: function to call with mocks applied
+    *args,**kwargs: arguments for 'func'
+
+    return_value: return from 'func'
+    """
+    delim = '.'
+    unwraps = []
+    for fname, kw in mocks.items():
+        if not isinstance(kw, dict):
+            kw = {'return_value': kw}
+        p = mock.patch(delim.join((prefix.rstrip(delim), fname)), **kw)
+        p.start()
+        unwraps.append(p)
+    try:
+        return func(*args, **kwargs)
+    finally:
+        for p in unwraps:
+            p.stop()
+
+
 try:
     skipIf = unittest.skipIf
 except AttributeError:
diff --git a/tests/unittests/test_data.py b/tests/unittests/test_data.py
index 55d9b93..dc64e8c 100644
--- a/tests/unittests/test_data.py
+++ b/tests/unittests/test_data.py
@@ -559,3 +559,54 @@ class TestConvertString(helpers.TestCase):
         text = "hi mom"
         msg = ud.convert_string(text)
         self.assertEqual(text, msg.get_payload(decode=False))
+
+
+class TestFetchBaseConfig(helpers.TestCase):
+
+    def test_only_builtin_gets_builtin2(self):
+        ret = helpers.wrap_and_call(
+            'cloudinit.stages.util',
+            {'read_conf_with_confd': None,
+             'read_cc_from_cmdline': None},
+            stages.fetch_base_config)
+        self.assertEqual(util.get_builtin_cfg(), ret)
+
+    def test_conf_d_overrides_defaults(self):
+        builtin = util.get_builtin_cfg()
+        test_key = sorted(builtin)[0]
+        test_value = 'test'
+        ret = helpers.wrap_and_call(
+            'cloudinit.stages.util',
+            {'read_conf_with_confd': {'return_value': {test_key: test_value}},
+             'read_cc_from_cmdline': None},
+            stages.fetch_base_config)
+        self.assertEqual(ret.get(test_key), test_value)
+        builtin[test_key] = test_value
+        self.assertEqual(ret, builtin)
+
+    def test_cmdline_overrides_defaults(self):
+        builtin = util.get_builtin_cfg()
+        test_key = sorted(builtin)[0]
+        test_value = 'test'
+        cmdline = '{}: "{}"'.format(test_key, test_value)
+        ret = helpers.wrap_and_call(
+            'cloudinit.stages.util',
+            {'read_cc_from_cmdline': {'return_value': cmdline},
+             'read_conf_with_confd': None},
+            stages.fetch_base_config)
+        self.assertEqual(ret.get(test_key), test_value)
+        builtin[test_key] = test_value
+        self.assertEqual(ret, builtin)
+
+    def test_cmdline_overrides_conf_d_and_defaults(self):
+        builtin = {'key1': 'value0', 'key3': 'other2'}
+        conf_d = {'key1': 'value1', 'key2': 'other1'}
+        cmdline = 'key3: "other3"\nkey2: "other2"'
+        ret = helpers.wrap_and_call(
+            'cloudinit.stages.util',
+            {'read_conf_with_confd': {'return_value': conf_d},
+             'get_builtin_cfg': {'return_value': builtin},
+             'read_cc_from_cmdline': {'return_value': cmdline}},
+            stages.fetch_base_config)
+        self.assertEqual(ret, {'key1': 'value1', 'key2': 'other2',
+                               'key3': 'other3'})

Follow ups