From 62631d30aae55a42b77d326af75d5e476d4baf36 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Fri, 21 Sep 2012 14:15:09 -0700 Subject: 1. Cleanup the user creation so that the distro class is responsible only for creating users and groups and normalizing a input configuration into a normalized format that splits up the user list, the group list and the default user listsand let the add user/group config module handle calling those methods to add its own users/groups and the default user (if any). 2. Also add in tests for this normalization process to ensure that it is pretty bug free and works with the different types of formats that users/groups/defaults + options can take. --- cloudinit/config/cc_users_groups.py | 77 ++++++++++++------------------------- 1 file changed, 24 insertions(+), 53 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_users_groups.py b/cloudinit/config/cc_users_groups.py index 418f3330..273c5068 100644 --- a/cloudinit/config/cc_users_groups.py +++ b/cloudinit/config/cc_users_groups.py @@ -16,63 +16,34 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . +from cloudinit import util + from cloudinit.settings import PER_INSTANCE frequency = PER_INSTANCE def handle(name, cfg, cloud, log, _args): - user_zero = None - - if 'groups' in cfg: - for group in cfg['groups']: - if isinstance(group, dict): - for name, values in group.iteritems(): - if isinstance(values, list): - cloud.distro.create_group(name, values) - elif isinstance(values, str): - cloud.distro.create_group(name, values.split(',')) - else: - cloud.distro.create_group(group, []) - - if 'users' in cfg: - user_zero = None - - for user_config in cfg['users']: - - # Handle the default user creation - if 'default' in user_config: - log.info("Creating default user") - - # Create the default user if so defined - try: - cloud.distro.add_default_user() - - if not user_zero: - user_zero = cloud.distro.get_default_user() - - except NotImplementedError: - - if user_zero == name: - user_zero = None - - log.warn("Distro has not implemented default user " - "creation. No default user will be created") - - elif isinstance(user_config, dict) and 'name' in user_config: - - name = user_config['name'] - if not user_zero: - user_zero = name - - # Make options friendly for distro.create_user - new_opts = {} - if isinstance(user_config, dict): - for opt in user_config: - new_opts[opt.replace('-', '_')] = user_config[opt] - - cloud.distro.create_user(**new_opts) - else: - # create user with no configuration - cloud.distro.create_user(user_config) + distro = cloud.distro + ((users, default_user), groups) = distro.normalize_users_groups(cfg) + for (name, members) in groups.items(): + distro.create_group(name, members) + + if default_user: + user = default_user['name'] + config = default_user['config'] + def_base_config = { + 'name': user, + '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]) + distro.create_user(**u_config) + + for (user, config) in users.items(): + distro.create_user(user, **config) -- 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 'cloudinit/config') 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 'cloudinit/config') 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 'cloudinit/config') 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 56d0585fd7d9804b82a1eb22faff8a6554b100b8 Mon Sep 17 00:00:00 2001 From: Joshua Harlow Date: Mon, 24 Sep 2012 20:49:29 -0700 Subject: Adjust the fingerprints to use this new user normalization function instead of the previous 'user' extraction. --- cloudinit/config/cc_ssh_authkey_fingerprints.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'cloudinit/config') diff --git a/cloudinit/config/cc_ssh_authkey_fingerprints.py b/cloudinit/config/cc_ssh_authkey_fingerprints.py index 23f5755a..36d439d2 100644 --- a/cloudinit/config/cc_ssh_authkey_fingerprints.py +++ b/cloudinit/config/cc_ssh_authkey_fingerprints.py @@ -23,6 +23,7 @@ from prettytable import PrettyTable from cloudinit import ssh_util from cloudinit import util +from cloudinit import distros def _split_hash(bin_hash): @@ -89,8 +90,9 @@ def handle(name, cfg, cloud, log, _args): log.debug(("Skipping module named %s, " "logging of ssh fingerprints disabled"), name) - user_name = util.get_cfg_option_str(cfg, "user", "ubuntu") hash_meth = util.get_cfg_option_str(cfg, "authkey_hash", "md5") - extract = ssh_util.extract_authorized_keys - (auth_key_fn, auth_key_entries) = extract(user_name, cloud.paths) - _pprint_key_entries(user_name, auth_key_fn, auth_key_entries, hash_meth) + extract_func = ssh_util.extract_authorized_keys + (users, _groups) = distros.normalize_users_groups(cfg, cloud.distro) + for (user_name, _cfg) in users.items(): + (auth_key_fn, auth_key_entries) = extract_func(user_name, cloud.paths) + _pprint_key_entries(user_name, auth_key_fn, auth_key_entries, hash_meth) -- cgit v1.2.3