From 712d019cba5afb535033e8228ad365071661ceb2 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 21 Sep 2012 14:17:42 -0700 Subject: Actually commit the test for user/group data normalization instead of forgetting about it. --- .../test_distros/test_user_data_normalize.py | 187 +++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 tests/unittests/test_distros/test_user_data_normalize.py (limited to 'tests/unittests') diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py new file mode 100644 index 00000000..6ff43a76 --- /dev/null +++ b/tests/unittests/test_distros/test_user_data_normalize.py @@ -0,0 +1,187 @@ +from mocker import MockerTestCase + +from cloudinit import distros +from cloudinit import helpers +from cloudinit import settings + + +class TestUGNormalize(MockerTestCase): + + def _make_distro(self, dtype, def_user=None, def_groups=None): + cfg = dict(settings.CFG_BUILTIN) + cfg['system_info']['distro'] = dtype + paths = helpers.Paths(cfg['system_info']['paths']) + distro_cls = distros.fetch(dtype) + distro = distro_cls(dtype, cfg['system_info'], paths) + if def_user: + distro.default_user = def_user + if def_groups: + distro.default_user_groups = def_groups + return distro + + def test_basic_groups(self): + distro = self._make_distro('ubuntu') + ug_cfg = { + 'groups': ['bob'], + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('bob', groups) + self.assertEquals({}, users) + self.assertEquals({}, def_user) + + def test_csv_groups(self): + distro = self._make_distro('ubuntu') + ug_cfg = { + 'groups': 'bob,joe,steve', + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('bob', groups) + self.assertIn('joe', groups) + self.assertIn('steve', groups) + self.assertEquals({}, users) + self.assertEquals({}, def_user) + + def test_more_groups(self): + distro = self._make_distro('ubuntu') + ug_cfg = { + 'groups': ['bob', 'joe', 'steve',] + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('bob', groups) + self.assertIn('joe', groups) + self.assertIn('steve', groups) + self.assertEquals({}, users) + self.assertEquals({}, def_user) + + def test_member_groups(self): + distro = self._make_distro('ubuntu') + ug_cfg = { + 'groups': { + 'bob': ['s'], + 'joe': [], + 'steve': [], + } + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('bob', groups) + self.assertEquals(['s'], groups['bob']) + self.assertEquals([], groups['joe']) + self.assertIn('joe', groups) + self.assertIn('steve', groups) + self.assertEquals({}, users) + self.assertEquals({}, def_user) + + def test_users_simple_dict(self): + distro = self._make_distro('ubuntu', 'bob') + ug_cfg = { + 'users': { + 'default': True, + } + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertEquals('bob', def_user['name']) + ug_cfg = { + 'users': { + 'default': 'yes', + } + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertEquals('bob', def_user['name']) + ug_cfg = { + 'users': { + 'default': '1', + } + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertEquals('bob', def_user['name']) + + def test_users_simple_dict_no(self): + distro = self._make_distro('ubuntu', 'bob') + ug_cfg = { + 'users': { + 'default': False, + } + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertEquals({}, def_user) + ug_cfg = { + 'users': { + 'default': 'no', + } + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertEquals({}, def_user) + + def test_users_simple(self): + distro = self._make_distro('ubuntu') + ug_cfg = { + 'users': [ + 'joe', + 'bob' + ], + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('joe', users) + self.assertIn('bob', users) + self.assertEquals({}, users['joe']) + self.assertEquals({}, users['bob']) + + def test_users_dict_default_additional(self): + distro = self._make_distro('ubuntu', 'bob') + ug_cfg = { + 'users': [ + {'name': 'default', 'blah': True} + ], + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('bob', def_user['name']) + self.assertEquals(",".join(distro.get_default_user_groups()), + def_user['config']['groups']) + self.assertEquals(True, + def_user['config']['blah']) + self.assertNotIn('bob', users) + + def test_users_dict_default(self): + distro = self._make_distro('ubuntu', 'bob') + ug_cfg = { + 'users': [ + 'default', + ], + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('bob', def_user['name']) + self.assertEquals(",".join(distro.get_default_user_groups()), + def_user['config']['groups']) + self.assertNotIn('bob', users) + + def test_users_dict_trans(self): + distro = self._make_distro('ubuntu') + ug_cfg = { + 'users': [ + {'name': 'joe', + 'tr-me': True}, + {'name': 'bob'}, + ], + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('joe', users) + self.assertIn('bob', users) + self.assertEquals({'tr_me': True}, users['joe']) + self.assertEquals({}, users['bob']) + + def test_users_dict(self): + distro = self._make_distro('ubuntu') + ug_cfg = { + 'users': [ + {'name': 'joe'}, + {'name': 'bob'}, + ], + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('joe', users) + self.assertIn('bob', users) + self.assertEquals({}, users['joe']) + self.assertEquals({}, users['bob']) + + + -- cgit v1.2.3 From 47d9df78264625207342e668a9120fa84b6ad355 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 21 Sep 2012 14:38:40 -0700 Subject: Also allow the user list to come in as a comma separated list so that its types match more of what the group list can be. --- cloudinit/distros/__init__.py | 2 ++ tests/unittests/test_distros/test_user_data_normalize.py | 11 +++++++++++ 2 files changed, 13 insertions(+) (limited to 'tests/unittests') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 4fb1d8c2..361d2c05 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -338,6 +338,8 @@ class Distro(object): raise TypeError(("Unmappable user value type %s" " for key %s") % (util.obj_name(v), k)) u_cfg = ad_ucfg + elif isinstance(u_cfg, (str, basestring)): + u_cfg = u_cfg.strip().split(",") users = {} for user_config in u_cfg: diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py index 6ff43a76..caf479cd 100644 --- a/tests/unittests/test_distros/test_user_data_normalize.py +++ b/tests/unittests/test_distros/test_user_data_normalize.py @@ -112,6 +112,17 @@ class TestUGNormalize(MockerTestCase): ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) self.assertEquals({}, def_user) + def test_users_simple_csv(self): + distro = self._make_distro('ubuntu') + ug_cfg = { + 'users': 'joe,bob', + } + ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + self.assertIn('joe', users) + self.assertIn('bob', users) + self.assertEquals({}, users['joe']) + self.assertEquals({}, users['bob']) + def test_users_simple(self): distro = self._make_distro('ubuntu') ug_cfg = { -- cgit v1.2.3 From 009faa0546ffbcadbbcaa9692d6842890e6f2e10 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 21 Sep 2012 15:12:11 -0700 Subject: Fix some docs + pylint warnings + log on default created in the module. --- cloudinit/config/cc_users_groups.py | 5 ++--- cloudinit/distros/__init__.py | 3 ++- doc/examples/cloud-config-user-groups.txt | 10 ++++++---- .../test_distros/test_user_data_normalize.py | 22 +++++++++++----------- 4 files changed, 21 insertions(+), 19 deletions(-) (limited to 'tests/unittests') diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index 273c5068..a6ce49ac 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -24,7 +24,6 @@ frequency = PER_INSTANCE def handle(name, cfg, cloud, log, _args): - distro = cloud.distro ((users, default_user), groups) = distro.normalize_users_groups(cfg) for (name, members) in groups.items(): @@ -34,7 +33,6 @@ def handle(name, cfg, cloud, log, _args): user = default_user['name'] config = default_user['config'] def_base_config = { - 'name': user, 'plain_text_passwd': user, 'home': "/home/%s" % user, 'shell': "/bin/bash", @@ -43,7 +41,8 @@ def handle(name, cfg, cloud, log, _args): 'sudo': "ALL=(ALL) NOPASSWD:ALL", } u_config = util.mergemanydict([def_base_config, config]) - distro.create_user(**u_config) + distro.create_user(user, **u_config) + log.info("Added default '%s' user with passwordless sudo", user) for (user, config) in users.items(): distro.create_user(user, **config) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 361d2c05..3de5be36 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -400,7 +400,8 @@ class Distro(object): } else: LOG.warn(("Distro has not provided a default user " - "creation. No default user will be normalized.")) + "for creation. No default user will be " + "normalized.")) users.pop('default', None) except NotImplementedError: LOG.warn(("Distro has not implemented default user " diff --git a/doc/examples/cloud-config-user-groups.txt b/doc/examples/cloud-config-user-groups.txt index 073fbd8f..1a46c540 100644 --- a/doc/examples/cloud-config-user-groups.txt +++ b/doc/examples/cloud-config-user-groups.txt @@ -1,11 +1,11 @@ -# add groups to the system +# Add groups to the system # The following example adds the ubuntu group with members foo and bar and # the group cloud-users. groups: - ubuntu: [foo,bar] - cloud-users -# add users to the system. Users are added after groups are added. +# Add users to the system. Users are added after groups are added. users: - default - name: foobar @@ -81,16 +81,18 @@ users: # directive. # system: Create the user as a system user. This means no home directory. # + # Default user creation: # -# Unless you define users, you will get a 'ubuntu' user on buntu systems with the +# Unless you define users, you will get a 'ubuntu' user on ubuntu systems with the # legacy permission (no password sudo, locked user, etc). If however, you want -# to have the ubuntu user in addition to other users, you need to instruct +# to have the 'ubuntu' user in addition to other users, you need to instruct # cloud-init that you also want the default user. To do this use the following # syntax: # users: # - default # - bob +# - .... # foobar: ... # # users[0] (the first user in users) overrides the user directive. diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py index caf479cd..d636bb84 100644 --- a/tests/unittests/test_distros/test_user_data_normalize.py +++ b/tests/unittests/test_distros/test_user_data_normalize.py @@ -78,21 +78,21 @@ class TestUGNormalize(MockerTestCase): 'default': True, } } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertEquals('bob', def_user['name']) ug_cfg = { 'users': { 'default': 'yes', } } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertEquals('bob', def_user['name']) ug_cfg = { 'users': { 'default': '1', } } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertEquals('bob', def_user['name']) def test_users_simple_dict_no(self): @@ -102,14 +102,14 @@ class TestUGNormalize(MockerTestCase): 'default': False, } } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertEquals({}, def_user) ug_cfg = { 'users': { 'default': 'no', } } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertEquals({}, def_user) def test_users_simple_csv(self): @@ -117,7 +117,7 @@ class TestUGNormalize(MockerTestCase): ug_cfg = { 'users': 'joe,bob', } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, _def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) @@ -131,7 +131,7 @@ class TestUGNormalize(MockerTestCase): 'bob' ], } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, _def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) @@ -144,7 +144,7 @@ class TestUGNormalize(MockerTestCase): {'name': 'default', 'blah': True} ], } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertIn('bob', def_user['name']) self.assertEquals(",".join(distro.get_default_user_groups()), def_user['config']['groups']) @@ -159,7 +159,7 @@ class TestUGNormalize(MockerTestCase): 'default', ], } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertIn('bob', def_user['name']) self.assertEquals(",".join(distro.get_default_user_groups()), def_user['config']['groups']) @@ -174,7 +174,7 @@ class TestUGNormalize(MockerTestCase): {'name': 'bob'}, ], } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, _def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({'tr_me': True}, users['joe']) @@ -188,7 +188,7 @@ class TestUGNormalize(MockerTestCase): {'name': 'bob'}, ], } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, _def_user), _groups) = distro.normalize_users_groups(ug_cfg) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) -- cgit v1.2.3 From 1278285241d017affa2d03f8023afaf2d35a9543 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Sun, 23 Sep 2012 12:39:22 -0700 Subject: Make the normalization a module level function since it has little dependence on the distros class itself. Readjust the using code to use this new module level function instead. --- cloudinit/config/cc_users_groups.py | 21 +- cloudinit/distros/__init__.py | 258 ++++++++++----------- .../test_distros/test_user_data_normalize.py | 35 +-- 3 files changed, 160 insertions(+), 154 deletions(-) (limited to 'tests/unittests') diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index a6ce49ac..13eb1102 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from cloudinit import distros from cloudinit import util from cloudinit.settings import PER_INSTANCE @@ -24,10 +25,20 @@ frequency = PER_INSTANCE def handle(name, cfg, cloud, log, _args): - distro = cloud.distro - ((users, default_user), groups) = distro.normalize_users_groups(cfg) + def_u = None + def_u_gs = None + try: + def_u = cloud.distro.get_default_user() + def_u_gs = cloud.distro.get_default_user_groups() + except NotImplementedError: + log.warn(("Distro has not implemented default user " + "creation. No default user will be added.")) + + ((users, default_user), groups) = distros.normalize_users_groups(cfg, + def_u, + def_u_gs) for (name, members) in groups.items(): - distro.create_group(name, members) + cloud.distro.create_group(name, members) if default_user: user = default_user['name'] @@ -41,8 +52,8 @@ def handle(name, cfg, cloud, log, _args): 'sudo': "ALL=(ALL) NOPASSWD:ALL", } u_config = util.mergemanydict([def_base_config, config]) - distro.create_user(user, **u_config) + cloud.distro.create_user(user, **u_config) log.info("Added default '%s' user with passwordless sudo", user) for (user, config) in users.items(): - distro.create_user(user, **config) + cloud.distro.create_user(user, **config) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 3de5be36..b43d662e 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -180,13 +180,7 @@ class Distro(object): def get_default_user_groups(self): if not self.default_user_groups: return [] - def_groups = [] - if isinstance(self.default_user_groups, (str, basestring)): - def_groups = self.default_user_groups.split(",") - else: - def_groups = list(self.default_user_groups) - def_groups = list(sorted(set(def_groups))) - return def_groups + return _uniq_merge_sorted(self.default_user_groups) def create_user(self, name, **kwargs): """ @@ -294,133 +288,6 @@ class Distro(object): return True - def _normalize_groups(self, grp_cfg): - groups = {} - if isinstance(grp_cfg, (str, basestring)): - grp_cfg = grp_cfg.strip().split(",") - - if isinstance(grp_cfg, (list)): - for g in grp_cfg: - g = g.strip() - if g: - groups[g] = [] - elif isinstance(grp_cfg, (dict)): - for grp_name, grp_members in grp_cfg.items(): - if isinstance(grp_members, (str, basestring)): - r_grp_members = [] - for gc in grp_members.strip().split(','): - gc = gc.strip() - if gc and gc not in r_grp_members: - r_grp_members.append(gc) - grp_members = r_grp_members - elif not isinstance(grp_members, (list)): - raise TypeError(("Group member config must be list " - " or string types only and not %s") % - util.obj_name(grp_members)) - groups[grp_name] = grp_members - else: - raise TypeError(("Group config must be list, dict " - " or string types only and not %s") % - util.obj_name(grp_cfg)) - return groups - - def _normalize_users(self, u_cfg): - if isinstance(u_cfg, (dict)): - ad_ucfg = [] - for (k, v) in u_cfg.items(): - if isinstance(v, (bool, int, basestring, str)): - if util.is_true(v): - ad_ucfg.append(str(k)) - elif isinstance(v, (dict)): - v['name'] = k - ad_ucfg.append(v) - else: - raise TypeError(("Unmappable user value type %s" - " for key %s") % (util.obj_name(v), k)) - u_cfg = ad_ucfg - elif isinstance(u_cfg, (str, basestring)): - u_cfg = u_cfg.strip().split(",") - - users = {} - for user_config in u_cfg: - if isinstance(user_config, (str, basestring)): - for u in user_config.strip().split(","): - u = u.strip() - if u and u not in users: - users[u] = {} - elif isinstance(user_config, (dict)): - if 'name' in user_config: - n = user_config.pop('name') - prev_config = users.get(n) or {} - users[n] = util.mergemanydict([prev_config, - user_config]) - else: - # Assume the default user then - prev_config = users.get('default') or {} - users['default'] = util.mergemanydict([prev_config, - user_config]) - elif isinstance(user_config, (bool, int)): - pass - else: - raise TypeError(("User config must be dictionary " - " or string types only and not %s") % - util.obj_name(user_config)) - - # Ensure user options are in the right python friendly format - if users: - c_users = {} - for (uname, uconfig) in users.items(): - c_uconfig = {} - for (k, v) in uconfig.items(): - k = k.replace('-', '_').strip() - if k: - c_uconfig[k] = v - c_users[uname] = c_uconfig - users = c_users - - # Fixup the default user into the real - # default user name and extract it - default_user = {} - if users and 'default' in users: - try: - def_config = users.pop('default') - def_user = self.get_default_user() - def_groups = self.get_default_user_groups() - if def_user: - u_config = users.pop(def_user, None) or {} - u_groups = u_config.get('groups') or [] - if isinstance(u_groups, (str, basestring)): - u_groups = u_groups.strip().split(",") - u_groups.extend(def_groups) - u_groups = set([x.strip() for x in u_groups if x.strip()]) - u_config['groups'] = ",".join(sorted(u_groups)) - default_user = { - 'name': def_user, - 'config': util.mergemanydict([def_config, u_config]), - } - else: - LOG.warn(("Distro has not provided a default user " - "for creation. No default user will be " - "normalized.")) - users.pop('default', None) - except NotImplementedError: - LOG.warn(("Distro has not implemented default user " - "creation. No default user will be normalized.")) - users.pop('default', None) - - return (default_user, users) - - def normalize_users_groups(self, ug_cfg): - users = {} - groups = {} - default_user = {} - if 'groups' in ug_cfg: - groups = self._normalize_groups(ug_cfg['groups']) - - if 'users' in ug_cfg: - default_user, users = self._normalize_users(ug_cfg['users']) - return ((users, default_user), groups) - def write_sudo_rules(self, user, rules, @@ -521,6 +388,129 @@ def _get_arch_package_mirror_info(package_mirrors, arch): return default +def _uniq_merge_sorted(*lists): + return sorted(_uniq_merge(*lists)) + + +def _uniq_merge(*lists): + combined_list = [] + for a_list in lists: + if isinstance(a_list, (str, basestring)): + a_list = a_list.strip().split(",") + else: + a_list = [str(a) for a in a_list] + a_list = [a.strip() for a in a_list if a.strip()] + combined_list.extend(a_list) + uniq_list = [] + for a in combined_list: + if a in uniq_list: + continue + else: + uniq_list.append(a) + return uniq_list + + +def _normalize_groups(grp_cfg): + if isinstance(grp_cfg, (str, basestring, list)): + c_grp_cfg = {} + for i in _uniq_merge(grp_cfg): + c_grp_cfg[i] = [] + grp_cfg = c_grp_cfg + + groups = {} + if isinstance(grp_cfg, (dict)): + for (grp_name, grp_members) in grp_cfg.items(): + groups[grp_name] = _uniq_merge_sorted(grp_members) + else: + raise TypeError(("Group config must be list, dict " + " or string types only and not %s") % + util.obj_name(grp_cfg)) + return groups + + +def _normalize_users(u_cfg, def_user=None, def_user_groups=None): + if isinstance(u_cfg, (dict)): + ad_ucfg = [] + for (k, v) in u_cfg.items(): + if isinstance(v, (bool, int, basestring, str, float)): + if util.is_true(v): + ad_ucfg.append(str(k)) + elif isinstance(v, (dict)): + v['name'] = k + ad_ucfg.append(v) + else: + raise TypeError(("Unmappable user value type %s" + " for key %s") % (util.obj_name(v), k)) + u_cfg = ad_ucfg + elif isinstance(u_cfg, (str, basestring)): + u_cfg = _uniq_merge_sorted(u_cfg) + + users = {} + for user_config in u_cfg: + if isinstance(user_config, (str, basestring, list)): + for u in _uniq_merge(user_config): + if u and u not in users: + users[u] = {} + elif isinstance(user_config, (dict)): + if 'name' in user_config: + n = user_config.pop('name') + prev_config = users.get(n) or {} + users[n] = util.mergemanydict([prev_config, + user_config]) + else: + # Assume the default user then + prev_config = users.get('default') or {} + users['default'] = util.mergemanydict([prev_config, + user_config]) + else: + raise TypeError(("User config must be dictionary/list " + " or string types only and not %s") % + util.obj_name(user_config)) + + # Ensure user options are in the right python friendly format + if users: + c_users = {} + for (uname, uconfig) in users.items(): + c_uconfig = {} + for (k, v) in uconfig.items(): + k = k.replace('-', '_').strip() + if k: + c_uconfig[k] = v + c_users[uname] = c_uconfig + users = c_users + + # Fixup the default user into the real + # default user name and extract it + default_user = {} + if users and 'default' in users: + def_config = users.pop('default') + def_groups = def_user_groups or [] + if def_user: + u_config = users.pop(def_user, None) or {} + u_groups = u_config.get('groups') or [] + u_groups = _uniq_merge_sorted(u_groups, def_groups) + u_config['groups'] = ",".join(u_groups) + default_user = { + 'name': def_user, + 'config': util.mergemanydict([def_config, u_config]), + } + + return (default_user, users) + + +def normalize_users_groups(cfg, def_user=None, def_user_groups=None): + users = {} + groups = {} + default_user = {} + if 'groups' in cfg: + groups = _normalize_groups(cfg['groups']) + if 'users' in cfg: + (default_user, users) = _normalize_users(cfg['users'], + def_user, + def_user_groups) + return ((users, default_user), groups) + + def fetch(name): locs = importer.find_module(name, ['', __name__], diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py index d636bb84..46733452 100644 --- a/tests/unittests/test_distros/test_user_data_normalize.py +++ b/tests/unittests/test_distros/test_user_data_normalize.py @@ -19,12 +19,17 @@ class TestUGNormalize(MockerTestCase): distro.default_user_groups = def_groups return distro + def _norm(self, cfg, distro): + def_u = distro.get_default_user() + def_u_gs = distro.get_default_user_groups() + return distros.normalize_users_groups(cfg, def_u, def_u_gs) + def test_basic_groups(self): distro = self._make_distro('ubuntu') ug_cfg = { 'groups': ['bob'], } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, def_user), groups) = self._norm(ug_cfg, distro) self.assertIn('bob', groups) self.assertEquals({}, users) self.assertEquals({}, def_user) @@ -34,7 +39,7 @@ class TestUGNormalize(MockerTestCase): ug_cfg = { 'groups': 'bob,joe,steve', } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, def_user), groups) = self._norm(ug_cfg, distro) self.assertIn('bob', groups) self.assertIn('joe', groups) self.assertIn('steve', groups) @@ -46,7 +51,7 @@ class TestUGNormalize(MockerTestCase): ug_cfg = { 'groups': ['bob', 'joe', 'steve',] } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, def_user), groups) = self._norm(ug_cfg, distro) self.assertIn('bob', groups) self.assertIn('joe', groups) self.assertIn('steve', groups) @@ -62,7 +67,7 @@ class TestUGNormalize(MockerTestCase): 'steve': [], } } - ((users, def_user), groups) = distro.normalize_users_groups(ug_cfg) + ((users, def_user), groups) = self._norm(ug_cfg, distro) self.assertIn('bob', groups) self.assertEquals(['s'], groups['bob']) self.assertEquals([], groups['joe']) @@ -78,21 +83,21 @@ class TestUGNormalize(MockerTestCase): 'default': True, } } - ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = self._norm(ug_cfg, distro) self.assertEquals('bob', def_user['name']) ug_cfg = { 'users': { 'default': 'yes', } } - ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = self._norm(ug_cfg, distro) self.assertEquals('bob', def_user['name']) ug_cfg = { 'users': { 'default': '1', } } - ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = self._norm(ug_cfg, distro) self.assertEquals('bob', def_user['name']) def test_users_simple_dict_no(self): @@ -102,14 +107,14 @@ class TestUGNormalize(MockerTestCase): 'default': False, } } - ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = self._norm(ug_cfg, distro) self.assertEquals({}, def_user) ug_cfg = { 'users': { 'default': 'no', } } - ((_users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((_users, def_user), _groups) = self._norm(ug_cfg, distro) self.assertEquals({}, def_user) def test_users_simple_csv(self): @@ -117,7 +122,7 @@ class TestUGNormalize(MockerTestCase): ug_cfg = { 'users': 'joe,bob', } - ((users, _def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((users, _def_user), _groups) = self._norm(ug_cfg, distro) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) @@ -131,7 +136,7 @@ class TestUGNormalize(MockerTestCase): 'bob' ], } - ((users, _def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((users, _def_user), _groups) = self._norm(ug_cfg, distro) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) @@ -144,7 +149,7 @@ class TestUGNormalize(MockerTestCase): {'name': 'default', 'blah': True} ], } - ((users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((users, def_user), _groups) = self._norm(ug_cfg, distro) self.assertIn('bob', def_user['name']) self.assertEquals(",".join(distro.get_default_user_groups()), def_user['config']['groups']) @@ -159,7 +164,7 @@ class TestUGNormalize(MockerTestCase): 'default', ], } - ((users, def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((users, def_user), _groups) = self._norm(ug_cfg, distro) self.assertIn('bob', def_user['name']) self.assertEquals(",".join(distro.get_default_user_groups()), def_user['config']['groups']) @@ -174,7 +179,7 @@ class TestUGNormalize(MockerTestCase): {'name': 'bob'}, ], } - ((users, _def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((users, _def_user), _groups) = self._norm(ug_cfg, distro) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({'tr_me': True}, users['joe']) @@ -188,7 +193,7 @@ class TestUGNormalize(MockerTestCase): {'name': 'bob'}, ], } - ((users, _def_user), _groups) = distro.normalize_users_groups(ug_cfg) + ((users, _def_user), _groups) = self._norm(ug_cfg, distro) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) -- cgit v1.2.3 From 0be941f74f54ecafcb628451f531b90f30723fbc Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 24 Sep 2012 18:30:35 -0700 Subject: Refactor the ug normalization to take in a distro and produce a user and group list. Clean this up to be simpler as well as handle the old 'user' case when it exists in configuration. --- cloudinit/config/cc_users_groups.py | 29 +----- cloudinit/distros/__init__.py | 80 ++++++++++------ .../test_distros/test_user_data_normalize.py | 103 ++++++++++++++------- 3 files changed, 122 insertions(+), 90 deletions(-) (limited to 'tests/unittests') diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index 13eb1102..464f55c3 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -25,35 +25,8 @@ frequency = PER_INSTANCE def handle(name, cfg, cloud, log, _args): - def_u = None - def_u_gs = None - try: - def_u = cloud.distro.get_default_user() - def_u_gs = cloud.distro.get_default_user_groups() - except NotImplementedError: - log.warn(("Distro has not implemented default user " - "creation. No default user will be added.")) - - ((users, default_user), groups) = distros.normalize_users_groups(cfg, - def_u, - def_u_gs) + (users, groups) = distros.normalize_users_groups(cfg, cloud.distro) for (name, members) in groups.items(): cloud.distro.create_group(name, members) - - if default_user: - user = default_user['name'] - config = default_user['config'] - def_base_config = { - 'plain_text_passwd': user, - 'home': "/home/%s" % user, - 'shell': "/bin/bash", - 'lock_passwd': True, - 'gecos': "%s%s" % (user.title()), - 'sudo': "ALL=(ALL) NOPASSWD:ALL", - } - u_config = util.mergemanydict([def_base_config, config]) - cloud.distro.create_user(user, **u_config) - log.info("Added default '%s' user with passwordless sudo", user) - for (user, config) in users.items(): cloud.distro.create_user(user, **config) diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index b43d662e..7340fa84 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -175,12 +175,20 @@ class Distro(object): return False def get_default_user(self): - return self.default_user - - def get_default_user_groups(self): - if not self.default_user_groups: - return [] - return _uniq_merge_sorted(self.default_user_groups) + if not self.default_user: + return None + user_cfg = { + 'name': self.default_user, + 'plain_text_passwd': self.default_user, + 'home': "/home/%s" % (self.default_user), + 'shell': "/bin/bash", + 'lock_passwd': True, + 'gecos': "%s" % (self.default_user.title()), + 'sudo': "ALL=(ALL) NOPASSWD:ALL", + } + if self.default_user_groups: + user_cfg['groups'] = _uniq_merge_sorted(self.default_user_groups) + return user_cfg def create_user(self, name, **kwargs): """ @@ -428,7 +436,7 @@ def _normalize_groups(grp_cfg): return groups -def _normalize_users(u_cfg, def_user=None, def_user_groups=None): +def _normalize_users(u_cfg, def_user_cfg=None): if isinstance(u_cfg, (dict)): ad_ucfg = [] for (k, v) in u_cfg.items(): @@ -480,35 +488,55 @@ def _normalize_users(u_cfg, def_user=None, def_user_groups=None): users = c_users # Fixup the default user into the real - # default user name and extract it - default_user = {} + # default user name and replace it... if users and 'default' in users: def_config = users.pop('default') - def_groups = def_user_groups or [] - if def_user: - u_config = users.pop(def_user, None) or {} - u_groups = u_config.get('groups') or [] - u_groups = _uniq_merge_sorted(u_groups, def_groups) - u_config['groups'] = ",".join(u_groups) - default_user = { - 'name': def_user, - 'config': util.mergemanydict([def_config, u_config]), - } + if def_user_cfg: + def_user = def_user_cfg.pop('name') + def_groups = def_user_cfg.pop('groups', []) + parsed_config = users.pop(def_user, {}) + users_groups = _uniq_merge_sorted(parsed_config.get('groups', []), + def_groups) + parsed_config['groups'] = ",".join(users_groups) + users[def_user] = util.mergemanydict([def_user_cfg, + def_config, + parsed_config]) - return (default_user, users) + return users -def normalize_users_groups(cfg, def_user=None, def_user_groups=None): +def normalize_users_groups(cfg, distro): users = {} groups = {} - default_user = {} if 'groups' in cfg: groups = _normalize_groups(cfg['groups']) + old_user = None + if 'user' in cfg: + old_user = str(cfg['user']) if 'users' in cfg: - (default_user, users) = _normalize_users(cfg['users'], - def_user, - def_user_groups) - return ((users, default_user), groups) + default_user_config = None + try: + default_user_config = distro.get_default_user() + except NotImplementedError: + LOG.warn(("Distro has not implemented default user " + "access. No default user will be normalized.")) + base_users = cfg['users'] + if isinstance(base_users, (list)): + if len(base_users) and old_user: + # The old user replaces user[0] + base_users[0] = {'name': old_user} + elif not base_users and old_user: + base.append({'name': old_user}) + elif isinstance(base_users, (dict)): + # Sorry order not possible + if old_user and old_user not in base_users: + base_users[old_user] = True + elif isinstance(base_users, (str, basestring)): + # Just append it on to be re-parsed later + if old_user: + base_users += ",%s" % (old_user) + users = _normalize_users(base_users, default_user_config) + return (users, groups) def fetch(name): diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py index 46733452..4a4e1a29 100644 --- a/tests/unittests/test_distros/test_user_data_normalize.py +++ b/tests/unittests/test_distros/test_user_data_normalize.py @@ -20,43 +20,38 @@ class TestUGNormalize(MockerTestCase): return distro def _norm(self, cfg, distro): - def_u = distro.get_default_user() - def_u_gs = distro.get_default_user_groups() - return distros.normalize_users_groups(cfg, def_u, def_u_gs) + return distros.normalize_users_groups(cfg, distro) def test_basic_groups(self): distro = self._make_distro('ubuntu') ug_cfg = { 'groups': ['bob'], } - ((users, def_user), groups) = self._norm(ug_cfg, distro) + (users, groups) = self._norm(ug_cfg, distro) self.assertIn('bob', groups) self.assertEquals({}, users) - self.assertEquals({}, def_user) def test_csv_groups(self): distro = self._make_distro('ubuntu') ug_cfg = { 'groups': 'bob,joe,steve', } - ((users, def_user), groups) = self._norm(ug_cfg, distro) + (users, groups) = self._norm(ug_cfg, distro) self.assertIn('bob', groups) self.assertIn('joe', groups) self.assertIn('steve', groups) self.assertEquals({}, users) - self.assertEquals({}, def_user) def test_more_groups(self): distro = self._make_distro('ubuntu') ug_cfg = { 'groups': ['bob', 'joe', 'steve',] } - ((users, def_user), groups) = self._norm(ug_cfg, distro) + (users, groups) = self._norm(ug_cfg, distro) self.assertIn('bob', groups) self.assertIn('joe', groups) self.assertIn('steve', groups) self.assertEquals({}, users) - self.assertEquals({}, def_user) def test_member_groups(self): distro = self._make_distro('ubuntu') @@ -67,14 +62,13 @@ class TestUGNormalize(MockerTestCase): 'steve': [], } } - ((users, def_user), groups) = self._norm(ug_cfg, distro) + (users, groups) = self._norm(ug_cfg, distro) self.assertIn('bob', groups) self.assertEquals(['s'], groups['bob']) self.assertEquals([], groups['joe']) self.assertIn('joe', groups) self.assertIn('steve', groups) self.assertEquals({}, users) - self.assertEquals({}, def_user) def test_users_simple_dict(self): distro = self._make_distro('ubuntu', 'bob') @@ -83,22 +77,22 @@ class TestUGNormalize(MockerTestCase): 'default': True, } } - ((_users, def_user), _groups) = self._norm(ug_cfg, distro) - self.assertEquals('bob', def_user['name']) + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('bob', users) ug_cfg = { 'users': { 'default': 'yes', } } - ((_users, def_user), _groups) = self._norm(ug_cfg, distro) - self.assertEquals('bob', def_user['name']) + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('bob', users) ug_cfg = { 'users': { 'default': '1', } } - ((_users, def_user), _groups) = self._norm(ug_cfg, distro) - self.assertEquals('bob', def_user['name']) + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('bob', users) def test_users_simple_dict_no(self): distro = self._make_distro('ubuntu', 'bob') @@ -107,22 +101,22 @@ class TestUGNormalize(MockerTestCase): 'default': False, } } - ((_users, def_user), _groups) = self._norm(ug_cfg, distro) - self.assertEquals({}, def_user) + (users, _groups) = self._norm(ug_cfg, distro) + self.assertEquals({}, users) ug_cfg = { 'users': { 'default': 'no', } } - ((_users, def_user), _groups) = self._norm(ug_cfg, distro) - self.assertEquals({}, def_user) + (users, _groups) = self._norm(ug_cfg, distro) + self.assertEquals({}, users) def test_users_simple_csv(self): distro = self._make_distro('ubuntu') ug_cfg = { 'users': 'joe,bob', } - ((users, _def_user), _groups) = self._norm(ug_cfg, distro) + (users, _groups) = self._norm(ug_cfg, distro) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) @@ -136,12 +130,51 @@ class TestUGNormalize(MockerTestCase): 'bob' ], } - ((users, _def_user), _groups) = self._norm(ug_cfg, distro) + (users, _groups) = self._norm(ug_cfg, distro) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) self.assertEquals({}, users['bob']) + def test_users_old_user(self): + distro = self._make_distro('ubuntu', 'bob') + ug_cfg = { + 'user': 'zetta', + 'users': 'default' + } + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('bob', users) + self.assertIn('zetta', users) + self.assertNotIn('default', users) + ug_cfg = { + 'user': 'zetta', + 'users': 'default, joe' + } + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('bob', users) + self.assertIn('joe', users) + self.assertIn('zetta', users) + self.assertNotIn('default', users) + ug_cfg = { + 'user': 'zetta', + 'users': ['bob', 'joe'] + } + (users, _groups) = self._norm(ug_cfg, distro) + self.assertNotIn('bob', users) + self.assertIn('joe', users) + self.assertIn('zetta', users) + ug_cfg = { + 'user': 'zetta', + 'users': { + 'bob': True, + 'joe': True, + } + } + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('bob', users) + self.assertIn('joe', users) + self.assertIn('zetta', users) + def test_users_dict_default_additional(self): distro = self._make_distro('ubuntu', 'bob') ug_cfg = { @@ -149,13 +182,12 @@ class TestUGNormalize(MockerTestCase): {'name': 'default', 'blah': True} ], } - ((users, def_user), _groups) = self._norm(ug_cfg, distro) - self.assertIn('bob', def_user['name']) - self.assertEquals(",".join(distro.get_default_user_groups()), - def_user['config']['groups']) + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('bob', users) + self.assertEquals(",".join(distro.get_default_user()['groups']), + users['bob']['groups']) self.assertEquals(True, - def_user['config']['blah']) - self.assertNotIn('bob', users) + users['bob']['blah']) def test_users_dict_default(self): distro = self._make_distro('ubuntu', 'bob') @@ -164,11 +196,10 @@ class TestUGNormalize(MockerTestCase): 'default', ], } - ((users, def_user), _groups) = self._norm(ug_cfg, distro) - self.assertIn('bob', def_user['name']) - self.assertEquals(",".join(distro.get_default_user_groups()), - def_user['config']['groups']) - self.assertNotIn('bob', users) + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('bob', users) + self.assertEquals(",".join(distro.get_default_user()['groups']), + users['bob']['groups']) def test_users_dict_trans(self): distro = self._make_distro('ubuntu') @@ -179,7 +210,7 @@ class TestUGNormalize(MockerTestCase): {'name': 'bob'}, ], } - ((users, _def_user), _groups) = self._norm(ug_cfg, distro) + (users, _groups) = self._norm(ug_cfg, distro) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({'tr_me': True}, users['joe']) @@ -193,7 +224,7 @@ class TestUGNormalize(MockerTestCase): {'name': 'bob'}, ], } - ((users, _def_user), _groups) = self._norm(ug_cfg, distro) + (users, _groups) = self._norm(ug_cfg, distro) self.assertIn('joe', users) self.assertIn('bob', users) self.assertEquals({}, users['joe']) -- cgit v1.2.3 From a2c6279d303a3b85625404653c7ab8081281ee18 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 24 Sep 2012 20:33:13 -0700 Subject: Handle the case where 'user' is defined but 'users' isn't. --- cloudinit/distros/__init__.py | 11 +++++++++-- tests/unittests/test_distros/test_user_data_normalize.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'tests/unittests') diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py index 13e4fd44..4cf8f745 100644 --- a/cloudinit/distros/__init__.py +++ b/cloudinit/distros/__init__.py @@ -507,13 +507,20 @@ def _normalize_users(u_cfg, def_user_cfg=None): def normalize_users_groups(cfg, distro): + if not cfg: + cfg = {} users = {} groups = {} if 'groups' in cfg: groups = _normalize_groups(cfg['groups']) + + # Handle the previous style of doing this... old_user = None - if 'user' in cfg: + if 'user' in cfg and cfg['user']: old_user = str(cfg['user']) + if not 'users' in cfg: + cfg['users'] = old_user + old_user = None if 'users' in cfg: default_user_config = None try: @@ -527,7 +534,7 @@ def normalize_users_groups(cfg, distro): # The old user replaces user[0] base_users[0] = {'name': old_user} elif not base_users and old_user: - base.append({'name': old_user}) + base_users.append({'name': old_user}) elif isinstance(base_users, (dict)): # Sorry order not possible if old_user and old_user not in base_users: diff --git a/tests/unittests/test_distros/test_user_data_normalize.py b/tests/unittests/test_distros/test_user_data_normalize.py index 4a4e1a29..890d8f05 100644 --- a/tests/unittests/test_distros/test_user_data_normalize.py +++ b/tests/unittests/test_distros/test_user_data_normalize.py @@ -174,6 +174,16 @@ class TestUGNormalize(MockerTestCase): self.assertIn('bob', users) self.assertIn('joe', users) self.assertIn('zetta', users) + ug_cfg = { + 'user': 'zetta', + } + (users, _groups) = self._norm(ug_cfg, distro) + self.assertIn('zetta', users) + ug_cfg = { + } + (users, groups) = self._norm(ug_cfg, distro) + self.assertEquals({}, users) + self.assertEquals({}, groups) def test_users_dict_default_additional(self): distro = self._make_distro('ubuntu', 'bob') -- cgit v1.2.3