Merge lp:~nealpzhang/juju-ci-tools/assess_multi_users into lp:juju-ci-tools

Proposed by Leo Zhang
Status: Merged
Merge reported by: Leo Zhang
Merged at revision: not available
Proposed branch: lp:~nealpzhang/juju-ci-tools/assess_multi_users
Merge into: lp:juju-ci-tools
Diff against target: 958 lines (+529/-198)
7 files modified
assess_container_networking.py (+1/-1)
assess_unregister.py (+1/-2)
assess_user_grant_revoke.py (+264/-91)
jujupy.py (+61/-2)
tests/test_assess_unregister.py (+7/-6)
tests/test_assess_user_grant_revoke.py (+85/-93)
tests/test_jujupy.py (+110/-3)
To merge this branch: bzr merge lp:~nealpzhang/juju-ci-tools/assess_multi_users
Reviewer Review Type Date Requested Status
Nicholas Skaggs (community) Approve
Aaron Bentley (community) Approve
Review via email: mp+299463@code.launchpad.net

Commit message

Added multi-users' tests

Description of the change

Added tests for multi-users.

To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

See comments inline.

The big thing is you can't store the user name on the backing state, because the backing state should be shared by all clients.

review: Needs Fixing
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Added a few more comments.

review: Needs Fixing
Revision history for this message
Aaron Bentley (abentley) wrote :

This is improved, but there are still some issues, as noted below.

review: Needs Fixing
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

Again some more comments.

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

More comments. A minor quibble, but I would like to see your assess_* functions be named assert_*. The only assess_* function should be the primary test function in following with project naming.

Also, try to format your code in paragraphs. Feel free to use spacing to logically group statements together. Currently the compressed nature of the code makes it harder to read.

Finally, it would be nice to better frame your assert functions so conditionals are not required. Instead clear expectations of what a user should and should not be able to do (based purely upon there permission levels), which you should pass in. You should run the asserts for all users -- even those who you expect to not be able to perform an action.

Revision history for this message
Leo Zhang (nealpzhang) wrote :

Aaron, Nicholas:

New updates after review.

Thanks

Revision history for this message
Aaron Bentley (abentley) wrote :

In general, it is confusing to provide "assertion" as a variable name. Assertions are statements, not boolean variables. I think "has_permission" would work as a variable name everywhere you've used "assertion".

Please be clear about whether you're testing model or controller permissions. assert_read is really assert_read_model, assert_write is really assert_write_model, and assert_admin is really assert_admin_controller.

Please use narrow try/except blocks. Don't include anything in the try block unless you expect it to raise the exception. So for example:

        code = ''.join(random.choice(
            string.ascii_letters + string.digits) for _ in xrange(4))
        try:
            client.add_user(permission + code, permissions='read')
        except subprocess.CalledProcessError:

You don't expect random.choice or xrange to raise CalledProcessError, so don't put them in the try block. If they do raise CalledProcessError, something very strange is happening and your test should fail with a traceback.

There's some wiggle room. It would be annoying to have separate try/except blocks for each child.expect() in case it raised pexpect.TIMEOUT, so putting a series of expect/sendline calls together in a single try/except block is fine. But I don't think child.isalive can raise pexpect.TIMEOUT, so it should go outside the try/except block.

It would be nice to check stderr to make sure that it is really a permissions error.

review: Needs Fixing
Revision history for this message
Leo Zhang (nealpzhang) wrote :

Aaron/Nicholas,

New updates after review.

Thanks

Revision history for this message
Aaron Bentley (abentley) wrote :

Some style changes to fix, but otherwise, I think this is landable.

review: Approve
Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

A couple final comments. Please address them, but you have my approval. I don't need to re-review before merging. Thanks for working on this!

I'll note remove user is landing, and this test should be updated to support it as well (but not i this MP).

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'assess_container_networking.py'
2--- assess_container_networking.py 2016-06-13 20:06:40 +0000
3+++ assess_container_networking.py 2016-07-25 20:33:36 +0000
4@@ -299,7 +299,7 @@
5 d = re.search(r'^default\s+via\s+([\d\.]+)\s+', routes, re.MULTILINE)
6 if d:
7 rc = client.juju('ssh', ('--proxy', target,
8- 'ping -c1 -q ' + d.group(1)), check=False)
9+ 'ping -c1 -q ' + d.group(1)), check=False)
10 if rc != 0:
11 raise ValueError('%s unable to ping default route' % target)
12 else:
13
14=== modified file 'assess_unregister.py'
15--- assess_unregister.py 2016-06-21 05:59:02 +0000
16+++ assess_unregister.py 2016-07-25 20:33:36 +0000
17@@ -25,7 +25,6 @@
18
19 from assess_user_grant_revoke import (
20 User,
21- register_user,
22 )
23 from deploy_stack import (
24 BootstrapManager,
25@@ -48,7 +47,7 @@
26 user = User('testuser', 'read', [])
27 user_controller_name = '{}_controller'.format(user.name)
28 with temp_dir() as fake_home:
29- user_client = register_user(user, client, fake_home)
30+ user_client = client.register_user(user, fake_home)
31 assert_controller_list(
32 user_client,
33 [user_controller_name])
34
35=== modified file 'assess_user_grant_revoke.py'
36--- assess_user_grant_revoke.py 2016-07-06 17:55:43 +0000
37+++ assess_user_grant_revoke.py 2016-07-25 20:33:36 +0000
38@@ -11,7 +11,11 @@
39
40 import argparse
41 from collections import namedtuple
42+import copy
43+import json
44 import logging
45+import random
46+import string
47 import subprocess
48 import sys
49
50@@ -20,12 +24,11 @@
51 from deploy_stack import (
52 BootstrapManager,
53 )
54-
55-from jujupy import Controller
56 from utility import (
57 add_basic_testing_arguments,
58 configure_logging,
59 temp_dir,
60+ wait_for_removed_services,
61 )
62
63 __metaclass__ = type
64@@ -36,109 +39,279 @@
65 User = namedtuple('User', ['name', 'permissions', 'expect'])
66
67
68+user_list_admin = [{"user-name": "admin", "display-name": "admin"}]
69+user_list_admin_read = copy.deepcopy(user_list_admin)
70+# Created user has no display name, bug 1606354
71+user_list_admin_read.append({"user-name": "readuser", "display-name": ""})
72+user_list_admin_read_write = copy.deepcopy(user_list_admin_read)
73+# bug 1606354
74+user_list_admin_read_write.append({"user-name": "writeuser",
75+ "display-name": ""})
76+user_list_all = copy.deepcopy(user_list_admin_read_write)
77+# bug 1606354
78+user_list_all.append({"user-name": "adminuser", "display-name": ""})
79+share_list_admin = {"admin@local": {"display-name": "admin",
80+ "access": "admin"}}
81+share_list_admin_read = copy.deepcopy(share_list_admin)
82+share_list_admin_read["readuser@local"] = {"access": "read"}
83+share_list_admin_read_write = copy.deepcopy(share_list_admin_read)
84+share_list_admin_read_write["writeuser@local"] = {"access": "write"}
85+del share_list_admin_read_write['readuser@local']
86+share_list_all = copy.deepcopy(share_list_admin_read_write)
87+share_list_all["adminuser@local"] = {"access": "admin"}
88+share_list_all["writeuser@local"]["access"] = "read"
89+
90+
91 # This needs refactored out to utility
92 class JujuAssertionError(AssertionError):
93 """Exception for juju assertion failures."""
94
95
96-def register_user(user, client, fake_home):
97- """Register `user` for the `client` return the cloned client used."""
98- # needs support to passing register command with arguments
99- # refactor once supported, bug 1573099
100+def list_users(client):
101+ """Test listing all the users"""
102+ users_list = json.loads(client.get_juju_output('list-users', '--format',
103+ 'json', include_e=False))
104+ for user in users_list:
105+ # Pop date-created and last-connection if existed for comparison
106+ user.pop("date-created", None)
107+ user.pop("last-connection", None)
108+ return users_list
109+
110+
111+def list_shares(client):
112+ """Test listing users' shares"""
113+ share_list = json.loads(client.get_juju_output('list-shares', '--format',
114+ 'json', include_e=False))
115+ for key, value in share_list.iteritems():
116+ # Pop last-connection if existed for comparison
117+ value.pop("last-connection", None)
118+ return share_list
119+
120+
121+def show_user(client):
122+ """Test showing a user's status"""
123+ user_status = json.loads(client.get_juju_output('show-user', '--format',
124+ 'json', include_e=False))
125+ # Pop date-created and last-connection if existed for comparison
126+ user_status.pop("date-created", None)
127+ user_status.pop("last-connection", None)
128+ return user_status
129+
130+
131+def assert_read_model(client, permission, has_permission):
132+ """Test if the user has or doesn't have the read permission"""
133+ if has_permission:
134+ try:
135+ client.show_status()
136+ except subprocess.CalledProcessError:
137+ raise JujuAssertionError(
138+ 'User could not check status with {} permission'.format(
139+ permission))
140+ else:
141+ try:
142+ client.show_status()
143+ except subprocess.CalledProcessError:
144+ pass
145+ else:
146+ raise JujuAssertionError(
147+ 'User checked status without {} permission'.format(permission))
148+
149+
150+def assert_write_model(client, permission, has_permission):
151+ """Test if the user has or doesn't have the write permission"""
152+ if has_permission:
153+ try:
154+ client.deploy('cs:ubuntu')
155+ except subprocess.CalledProcessError:
156+ raise JujuAssertionError(
157+ 'User could not deploy with {} permission'.format(permission))
158+ else:
159+ client.remove_service('ubuntu')
160+ else:
161+ try:
162+ client.deploy('cs:ubuntu')
163+ except subprocess.CalledProcessError:
164+ pass
165+ else:
166+ raise JujuAssertionError(
167+ 'User deployed without {} permission'.format(permission))
168+
169+
170+def assert_admin_controller(client, permission, has_permission):
171+ """Test if the user has or doesn't have the admin permission"""
172+ if has_permission:
173+ code = ''.join(random.choice(
174+ string.ascii_letters + string.digits) for _ in xrange(4))
175+ try:
176+ client.add_user(permission + code, permissions='read')
177+ except subprocess.CalledProcessError:
178+ raise JujuAssertionError(
179+ 'User could not add user with {} permission'.format(
180+ permission))
181+ else:
182+ try:
183+ client.add_user(permission + 'false', permissions='read')
184+ except subprocess.CalledProcessError:
185+ pass
186+ else:
187+ raise JujuAssertionError(
188+ 'User added user without {} permission'.format(permission))
189+
190+
191+def assert_user_permissions(user, user_client, controller_client):
192+ """Test if users' permissions are within expectations"""
193+ expect = iter(user.expect)
194+ permission = user.permissions
195+ assert_read_model(user_client, permission, expect.next())
196+ assert_write_model(user_client, permission, expect.next())
197+ assert_admin_controller(user_client, permission, expect.next())
198+
199+ log.debug("Revoking %s permission from %s" % (user.permissions, user.name))
200+ controller_client.revoke(user.name, permissions=user.permissions)
201+
202+ assert_read_model(user_client, permission, expect.next())
203+ assert_write_model(user_client, permission, expect.next())
204+ assert_admin_controller(user_client, permission, expect.next())
205+
206+
207+def assert_users_shares(controller_client, client, user):
208+ """Test if user_list and share_list are expected"""
209+ if user.name == 'readuser':
210+ user_list_expected = user_list_admin_read
211+ share_list_expected = share_list_admin_read
212+ else:
213+ user_list_expected = user_list_admin_read_write
214+ share_list_expected = share_list_all
215+ user_list = list_users(controller_client)
216+ share_list = list_shares(controller_client)
217+ if sorted(user_list) != sorted(user_list_expected):
218+ raise JujuAssertionError(user_list)
219+ if sorted(share_list) != sorted(share_list_expected):
220+ raise JujuAssertionError(share_list)
221+
222+
223+def assert_change_password(client, user):
224+ """Test changing user's password"""
225+ try:
226+ child = client.expect('change-user-password', (user.name,),
227+ include_e=False)
228+ child.expect('(?i)password')
229+ child.sendline(user.name + '_password_2')
230+ child.expect('(?i)password')
231+ child.sendline(user.name + '_password_2')
232+ child.expect(pexpect.EOF)
233+ except pexpect.TIMEOUT:
234+ raise JujuAssertionError(
235+ 'Changing user password failed: pexpect session timed out')
236+ if child.isalive():
237+ raise JujuAssertionError(
238+ 'Changing user password failed: pexpect session still alive')
239+
240+
241+def assert_disable_enable(controller_client, user):
242+ """Test disabling and enabling users"""
243+ controller_client.disable_user(user.name)
244+ user_list = list_users(controller_client)
245+ if sorted(user_list) != sorted(user_list_admin_read):
246+ raise JujuAssertionError(user_list)
247+ controller_client.enable_user(user.name)
248+ user_list = list_users(controller_client)
249+ if sorted(user_list) != sorted(user_list_admin_read_write):
250+ raise JujuAssertionError(user_list)
251+
252+
253+def assert_logout_login(controller_client, user_client, user, fake_home):
254+ """Test users' login and logout"""
255+ user_client.logout()
256+ user_list = list_users(controller_client)
257+ if sorted(user_list) != sorted(user_list_admin_read):
258+ raise JujuAssertionError(user_list)
259 username = user.name
260 controller_name = '{}_controller'.format(username)
261- token = client.add_user(username, permissions=user.permissions)
262- user_client, user_env = create_cloned_environment(
263- client, fake_home, controller_name)
264-
265+ client = controller_client.create_cloned_environment(
266+ fake_home, controller_name)
267 try:
268- child = user_client.expect(
269- 'register', (token), extra_env=user_env, include_e=False)
270- child.expect('(?i)name')
271- child.sendline(controller_name)
272- child.expect('(?i)password')
273- child.sendline(username + '_password')
274- child.expect('(?i)password')
275- child.sendline(username + '_password')
276+ child = client.expect('login', (user.name, '-c', controller_name),
277+ include_e=False)
278+ child.expect('(?i)password')
279+ child.sendline(user.name + '_password_2')
280 child.expect(pexpect.EOF)
281 if child.isalive():
282 raise JujuAssertionError(
283- 'Registering user failed: pexpect session still alive')
284+ 'Login user failed: pexpect session still alive')
285 except pexpect.TIMEOUT:
286 raise JujuAssertionError(
287- 'Registering user failed: pexpect session timed out')
288- return user_client
289-
290-
291-def create_cloned_environment(client, cloned_juju_home, controller_name):
292- user_client = client.clone(env=client.env.clone())
293- user_client.env.juju_home = cloned_juju_home
294- # New user names the controller.
295- user_client.env.controller = Controller(controller_name)
296- user_client_env = user_client._shell_environ()
297- return user_client, user_client_env
298-
299-
300-def assert_read(client, permission):
301- if permission:
302- try:
303- client.show_status()
304- except subprocess.CalledProcessError:
305- raise JujuAssertionError(
306- 'User could not check status with read permission')
307- else:
308- try:
309- client.show_status()
310- except subprocess.CalledProcessError:
311- pass
312- else:
313- raise JujuAssertionError(
314- 'User checked status without read permission')
315-
316-
317-def assert_write(client, permission):
318- if permission:
319- try:
320- client.deploy('cs:ubuntu')
321- except subprocess.CalledProcessError:
322- raise JujuAssertionError(
323- 'User could not deploy with write permission')
324- else:
325- try:
326- client.deploy('cs:ubuntu')
327- except subprocess.CalledProcessError:
328- pass
329- else:
330- raise JujuAssertionError('User deployed without write permission')
331-
332-
333-def assert_user_permissions(user, user_client, admin_client):
334- expect = iter(user.expect)
335- assert_read(user_client, expect.next())
336- assert_write(user_client, expect.next())
337-
338- log.debug("Revoking %s permission from %s" % (user.permissions, user.name))
339- admin_client.revoke(user.name, permissions=user.permissions)
340-
341- assert_read(user_client, expect.next())
342- assert_write(user_client, expect.next())
343-
344-
345-def assess_user_grant_revoke(admin_client):
346- # Wait for the deployment to finish.
347- admin_client.wait_for_started()
348-
349+ 'Login user failed: pexpect session timed out')
350+
351+
352+def assert_read_user(controller_client, user):
353+ """Assess the operations of read user"""
354+ with temp_dir() as fake_home:
355+ user_client = controller_client.register_user(
356+ user, fake_home)
357+ user_list = list_users(controller_client)
358+ share_list = list_shares(controller_client)
359+ if sorted(user_list) != sorted(user_list_admin_read):
360+ raise JujuAssertionError(user_list)
361+ if sorted(share_list) != sorted(share_list_admin_read):
362+ raise JujuAssertionError(share_list)
363+ assert_change_password(user_client, user)
364+ assert_logout_login(controller_client, user_client, user, fake_home)
365+ assert_user_permissions(user, user_client, controller_client)
366+
367+
368+def assert_write_user(controller_client, user):
369+ """Assess the operations of write user"""
370+ with temp_dir() as fake_home:
371+ user_client = controller_client.register_user(
372+ user, fake_home)
373+ user_list = list_users(controller_client)
374+ share_list = list_shares(controller_client)
375+ if sorted(user_list) != sorted(user_list_admin_read_write):
376+ raise JujuAssertionError(user_list)
377+ if sorted(share_list) != sorted(share_list_admin_read_write):
378+ raise JujuAssertionError(share_list)
379+ assert_disable_enable(controller_client, user)
380+ assert_user_permissions(user, user_client, controller_client)
381+ wait_for_removed_services(user_client, 'cs:ubuntu')
382+
383+
384+def assert_admin_user(controller_client, user):
385+ """Assess the operations of admin user"""
386+ with temp_dir() as fake_home:
387+ user_client = controller_client.register_user(
388+ user, fake_home)
389+ user_list = list_users(controller_client)
390+ share_list = list_shares(controller_client)
391+ if sorted(user_list) != sorted(user_list_all):
392+ raise JujuAssertionError(user_list)
393+ if sorted(share_list) != sorted(share_list_all):
394+ raise JujuAssertionError(share_list)
395+ assert_user_permissions(user, user_client, controller_client)
396+
397+
398+def assess_user_grant_revoke(controller_client):
399+ """Test multi-users functionality"""
400+ controller_client.env.user_name = 'admin'
401 log.debug("Creating Users")
402- read_user = User('readuser', 'read', [True, False, False, False])
403- write_user = User('adminuser', 'write', [True, True, True, False])
404- users = [read_user, write_user]
405-
406- for user in users:
407- log.debug("Testing %s" % user.name)
408- with temp_dir() as fake_home:
409- user_client = register_user(
410- user, admin_client, fake_home)
411- assert_user_permissions(user, user_client, admin_client)
412+ read_user = User('readuser', 'read',
413+ [True, False, False, False, False, False])
414+ write_user = User('writeuser', 'write',
415+ [True, True, False, True, False, False])
416+ admin_user = User('adminuser', 'admin',
417+ [True, True, True, True, True, True])
418+ user_list = list_users(controller_client)
419+ share_list = list_shares(controller_client)
420+ user_status = show_user(controller_client)
421+ if sorted(user_list) != sorted(user_list_admin):
422+ raise JujuAssertionError(user_list)
423+ if share_list != share_list_admin:
424+ raise JujuAssertionError(share_list)
425+ if user_status != user_list_admin[0]:
426+ raise JujuAssertionError(user_status)
427+ assert_read_user(controller_client, read_user)
428+ assert_write_user(controller_client, write_user)
429+ assert_admin_user(controller_client, admin_user)
430
431
432 def parse_args(argv):
433
434=== modified file 'jujupy.py'
435--- jujupy.py 2016-07-22 18:12:55 +0000
436+++ jujupy.py 2016-07-25 20:33:36 +0000
437@@ -215,6 +215,7 @@
438
439 def __init__(self, environment, config=None, juju_home=None,
440 controller=None):
441+ self.user_name = None
442 if controller is None:
443 controller = Controller(environment)
444 self.controller = controller
445@@ -663,7 +664,7 @@
446 raise subprocess.CalledProcessError(retcode, full_args)
447
448 def get_juju_output(self, command, args, used_feature_flags,
449- juju_home, model=None, timeout=None):
450+ juju_home, model=None, timeout=None, user_name=None):
451 args = self.full_args(command, args, model, timeout)
452 env = self.shell_environ(used_feature_flags, juju_home)
453 log.debug(args)
454@@ -1136,7 +1137,7 @@
455 timeout = kwargs.get('timeout')
456 return self._backend.get_juju_output(
457 command, args, self.used_feature_flags, self.env.juju_home,
458- model, timeout)
459+ model, timeout, user_name=self.env.user_name)
460
461 def show_status(self):
462 """Print the status to output."""
463@@ -1807,6 +1808,64 @@
464 (name, provider,
465 'size={}'.format(size)))
466
467+ def disable_user(self, user_name):
468+ """Disable an user"""
469+ self.controller_juju('disable-user', (user_name,))
470+
471+ def enable_user(self, user_name):
472+ """Enable an user"""
473+ self.controller_juju('enable-user', (user_name,))
474+
475+ def logout(self):
476+ """Logout an user"""
477+ self.controller_juju('logout', ())
478+
479+ def register_user(self, user, juju_home):
480+ """Register `user` for the `client` return the cloned client used."""
481+ username = user.name
482+ controller_name = '{}_controller'.format(username)
483+
484+ model = self.env.environment
485+ token = self.add_user(username, models=model + ',controller',
486+ permissions=user.permissions)
487+ user_client = self.create_cloned_environment(juju_home,
488+ controller_name)
489+
490+ try:
491+ child = user_client.expect(
492+ 'register', (token), include_e=False)
493+ child.expect('(?i)name')
494+ child.sendline(username + '_controller')
495+ child.expect('(?i)password')
496+ child.sendline(username + '_password')
497+ child.expect('(?i)password')
498+ child.sendline(username + '_password')
499+ child.expect(pexpect.EOF)
500+ if child.isalive():
501+ raise Exception(
502+ 'Registering user failed: pexpect session still alive')
503+ except pexpect.TIMEOUT:
504+ raise Exception(
505+ 'Registering user failed: pexpect session timed out')
506+ user_client.env.user_name = username
507+ return user_client
508+
509+ def create_cloned_environment(self, cloned_juju_home, controller_name):
510+ """Create a cloned environment"""
511+ user_client = self.clone(env=self.env.clone())
512+ user_client.env.juju_home = cloned_juju_home
513+ # New user names the controller.
514+ user_client.env.controller = Controller(controller_name)
515+ # user_client_env = user_client._shell_environ()
516+ return user_client
517+
518+ def grant(self, user_name, permission, model=None):
519+ """Grant the user with a model."""
520+ if model is None:
521+ model = self.model_name
522+ self.juju('grant', (user_name, model, '--acl', permission),
523+ include_e=False)
524+
525
526 class EnvJujuClient2B8(EnvJujuClient):
527
528
529=== modified file 'tests/test_assess_unregister.py'
530--- tests/test_assess_unregister.py 2016-07-08 13:22:35 +0000
531+++ tests/test_assess_unregister.py 2016-07-25 20:33:36 +0000
532@@ -63,12 +63,13 @@
533
534 def test_unregister(self):
535 fake_user = Mock()
536- with patch.object(a_unreg, 'register_user',
537- return_value=fake_user):
538- with patch.object(a_unreg, 'assert_controller_list',
539- autospec=True) as mock_assert_list:
540- with patch.object(a_unreg, 'assert_switch_raises_error',
541- autospec=True):
542+ with patch('jujupy.EnvJujuClient.register_user',
543+ return_value=fake_user):
544+ with patch.object(
545+ a_unreg, 'assert_controller_list',
546+ autospec=True) as mock_assert_list:
547+ with patch.object(
548+ a_unreg, 'assert_switch_raises_error', autospec=True):
549 fake_client = Mock(wraps=fake_juju_client())
550 fake_client.env.controller.name = 'testing-controller'
551 fake_client.bootstrap()
552
553=== modified file 'tests/test_assess_user_grant_revoke.py'
554--- tests/test_assess_user_grant_revoke.py 2016-07-08 17:30:56 +0000
555+++ tests/test_assess_user_grant_revoke.py 2016-07-25 20:33:36 +0000
556@@ -10,18 +10,14 @@
557 import StringIO
558 from subprocess import CalledProcessError
559
560-import pexpect
561-
562 from assess_user_grant_revoke import (
563- assert_read,
564+ assert_read_model,
565 assert_user_permissions,
566- assert_write,
567+ assert_write_model,
568 assess_user_grant_revoke,
569- create_cloned_environment,
570 JujuAssertionError,
571 main,
572 parse_args,
573- register_user,
574 )
575 from jujupy import JUJU_DEV_FEATURE_FLAGS
576 from tests import (
577@@ -112,47 +108,60 @@
578 class TestAsserts(TestCase):
579
580 def test_assert_user_permissions(self):
581- user = namedtuple('user', ['name', 'permissions', 'expect'])
582- read_user = user('readuser', 'read', [True, False, False, False])
583- write_user = user('adminuser', 'write', [True, True, True, False])
584- users = [read_user, write_user]
585+ User = namedtuple('user', ['name', 'permissions', 'expect'])
586+ read_user = User('readuser', 'read',
587+ [True, False, False, False, False, False])
588+ write_user = User('writeuser', 'write',
589+ [True, True, False, True, False, False])
590+ admin_user = User('adminuser', 'admin',
591+ [True, True, True, True, False, False])
592+ users = [read_user, write_user, admin_user]
593
594 for user in users:
595 fake_client = fake_juju_client()
596 fake_admin_client = fake_juju_client()
597+ ac = patch("assess_user_grant_revoke.assert_admin_controller",
598+ return_value=True)
599 with patch("jujupy.EnvJujuClient.revoke", return_value=True):
600- with patch("assess_user_grant_revoke.assert_read",
601+ with patch("assess_user_grant_revoke.assert_read_model",
602 return_value=True) as read_mock:
603- with patch("assess_user_grant_revoke.assert_write",
604+ with patch("assess_user_grant_revoke.assert_write_model",
605 return_value=True) as write_mock:
606- assert_user_permissions(user, fake_client,
607- fake_admin_client)
608- self.assertEqual(read_mock.call_count, 2)
609- self.assertEqual(write_mock.call_count, 2)
610+ with ac as admin_mock:
611+ assert_user_permissions(user, fake_client,
612+ fake_admin_client)
613+ self.assertEqual(read_mock.call_count, 2)
614+ self.assertEqual(write_mock.call_count, 2)
615+ self.assertEqual(admin_mock.call_count, 2)
616
617- def test_assert_read(self):
618+ def test_assert_read_model(self):
619 fake_client = fake_juju_client()
620 with patch.object(fake_client, 'show_status', return_value=True):
621- assert_read(fake_client, True)
622+ assert_read_model(fake_client, 'read', True)
623 with self.assertRaises(JujuAssertionError):
624- assert_read(fake_client, False)
625+ assert_read_model(fake_client, 'read', False)
626 with patch.object(fake_client, 'show_status', return_value=False,
627 side_effect=CalledProcessError(None, None, None)):
628- assert_read(fake_client, False)
629+ assert_read_model(fake_client, 'read', False)
630 with self.assertRaises(JujuAssertionError):
631- assert_read(fake_client, True)
632+ assert_read_model(fake_client, 'read', True)
633
634- def test_assert_write(self):
635+ def test_assert_write_model(self):
636 fake_client = fake_juju_client()
637 with patch.object(fake_client, 'deploy', return_value=True):
638- assert_write(fake_client, True)
639+ with patch.object(fake_client, 'remove_service', autospec=True):
640+ assert_write_model(fake_client, 'write', True)
641 with self.assertRaises(JujuAssertionError):
642- assert_write(fake_client, False)
643+ with patch.object(fake_client, 'remove_service',
644+ autospec=True):
645+ assert_write_model(fake_client, 'write', False)
646 with patch.object(fake_client, 'deploy', return_value=False,
647 side_effect=CalledProcessError(None, None, None)):
648- assert_write(fake_client, False)
649+ assert_write_model(fake_client, 'write', False)
650 with self.assertRaises(JujuAssertionError):
651- assert_write(fake_client, True)
652+ with patch.object(fake_client, 'remove_service',
653+ autospec=True):
654+ assert_write_model(fake_client, 'write', True)
655
656
657 def make_fake_client():
658@@ -169,70 +178,53 @@
659 def test_user_grant_revoke(self):
660 fake_client = make_fake_client()
661 fake_client.bootstrap()
662-
663- user = namedtuple('user', ['name', 'permissions', 'expect'])
664- read_user = user('readuser', 'read', [True, False, False, False])
665- write_user = user('adminuser', 'write', [True, True, True, False])
666-
667- with patch("assess_user_grant_revoke.register_user",
668- return_value=True) as reg_mock:
669- with patch("assess_user_grant_revoke.assert_user_permissions",
670- autospec=True) as perm_mock:
671- assess_user_grant_revoke(fake_client)
672-
673- self.assertEqual(reg_mock.call_count, 2)
674- self.assertEqual(perm_mock.call_count, 2)
675-
676- read_user_call, write_user_call = perm_mock.call_args_list
677- read_user_args, read_user_kwargs = read_user_call
678- write_user_args, write_user_kwargs = write_user_call
679-
680- self.assertEqual(read_user_args[0], read_user)
681- self.assertEqual(read_user_args[2], fake_client)
682- self.assertEqual(write_user_args[0], write_user)
683- self.assertEqual(write_user_args[2], fake_client)
684-
685- def test_create_cloned_environment(self):
686- fake_client = make_fake_client()
687- fake_client.bootstrap()
688- fake_client_environ = fake_client._shell_environ()
689- controller_name = 'user_controller'
690- cloned, cloned_environ = create_cloned_environment(
691- fake_client,
692- 'fakehome',
693- controller_name
694- )
695- self.assertIs(fake_client.__class__, type(cloned))
696- self.assertEqual(cloned.env.juju_home, 'fakehome')
697- self.assertNotEqual(cloned_environ, fake_client_environ)
698- self.assertEqual(cloned_environ['JUJU_DATA'], 'fakehome')
699- self.assertEqual(cloned.env.controller.name, controller_name)
700- self.assertEqual(fake_client.env.controller.name, 'name')
701-
702- def test_register_user(self):
703- FakeUser = namedtuple('user', ['name', 'permissions'])
704- user = FakeUser('fakeuser', 'read')
705-
706- class FakeClient:
707- """Lightweight fake client for testing."""
708- def add_user(self, username, permissions):
709- return 'token'
710-
711- def expect(self, *args, **kwargs):
712- return PexpectInteraction(
713- [
714- user.name + '_controller',
715- user.name + '_password',
716- user.name + '_password',
717- pexpect.EOF],
718- [
719- '(?i)name',
720- '(?i)password',
721- '(?i)password',
722- pexpect.EOF])
723-
724- fake_client = FakeClient()
725- with patch(
726- 'assess_user_grant_revoke.create_cloned_environment',
727- return_value=(fake_client, {})):
728- register_user(user, fake_client, '/tmp/dir/path')
729+ assert_read_calls = [True, False, True, True, True, True]
730+ assert_write_calls = [False, False, True, False, True, True]
731+ assert_admin_calls = [False, False, False, False, True, True]
732+ cpass = patch("assess_user_grant_revoke.assert_change_password",
733+ autospec=True)
734+ able = patch("assess_user_grant_revoke.assert_disable_enable",
735+ autospec=True)
736+ log = patch("assess_user_grant_revoke.assert_logout_login",
737+ autospec=True)
738+ expect = patch("jujupy.EnvJujuClient.expect",
739+ autospec=True)
740+ read = patch("assess_user_grant_revoke.assert_read_model",
741+ autospec=True)
742+ write = patch("assess_user_grant_revoke.assert_write_model",
743+ autospec=True)
744+ admin = patch("assess_user_grant_revoke.assert_admin_controller",
745+ autospec=True)
746+ rm = patch("utility.wait_for_removed_services",
747+ autospec=True)
748+ with cpass as pass_mock, able as able_mock, log as log_mock:
749+ with read as read_mock, write as write_mock, admin as admin_mock:
750+ with expect as expect_mock:
751+ expect_mock.return_value.isalive.return_value = False
752+ with rm:
753+ assess_user_grant_revoke(fake_client)
754+
755+ self.assertEqual(pass_mock.call_count, 1)
756+ self.assertEqual(able_mock.call_count, 1)
757+ self.assertEqual(log_mock.call_count, 1)
758+
759+ self.assertEqual(read_mock.call_count, 6)
760+ self.assertEqual(write_mock.call_count, 6)
761+ self.assertEqual(admin_mock.call_count, 6)
762+
763+ read_calls = [
764+ call[0][2] for call in
765+ read_mock.call_args_list]
766+ write_calls = [
767+ call[0][2] for call in
768+ write_mock.call_args_list]
769+ admin_calls = [
770+ call[0][2] for call in
771+ admin_mock.call_args_list]
772+
773+ self.assertEqual(read_calls,
774+ assert_read_calls)
775+ self.assertEqual(write_calls,
776+ assert_write_calls)
777+ self.assertEqual(admin_calls,
778+ assert_admin_calls)
779
780=== modified file 'tests/test_jujupy.py'
781--- tests/test_jujupy.py 2016-07-25 18:08:15 +0000
782+++ tests/test_jujupy.py 2016-07-25 20:33:36 +0000
783@@ -127,6 +127,13 @@
784 def __init__(self):
785 self.state = 'not-bootstrapped'
786 self.models = {}
787+ self.users = {
788+ 'admin': {
789+ 'state': '',
790+ 'permission': 'write'
791+ }
792+ }
793+ self.shares = ['admin']
794
795 def add_model(self, name):
796 state = FakeEnvironmentState()
797@@ -471,6 +478,45 @@
798 self.controller_state.models.values()]
799 return {'models': [{'name': n} for n in model_names]}
800
801+ def list_users(self):
802+ user_names = [name for name in
803+ self.controller_state.users.keys()]
804+ user_list = []
805+ for n in user_names:
806+ if n == 'admin':
807+ append_dict = {'user-name': n, 'display-name': n}
808+ else:
809+ append_dict = {'user-name': n, 'display-name': ''}
810+ user_list.append(append_dict)
811+ return user_list
812+
813+ def show_user(self, user_name):
814+ if user_name is None:
815+ raise Exception("No user specified")
816+ if user_name == 'admin':
817+ user_status = {'user-name': user_name, 'display-name': user_name}
818+ else:
819+ user_status = {'user-name': user_name, 'display-name': ''}
820+ return user_status
821+
822+ def list_shares(self):
823+ share_names = self.controller_state.shares
824+ permissions = []
825+ for key, value in self.controller_state.users.iteritems():
826+ if key in share_names:
827+ permissions.append(value['permission'])
828+ share_list = {}
829+ for i, (share_name, permission) in enumerate(
830+ zip(share_names, permissions)):
831+ name = share_name + '@local'
832+ share_list[name] = {'display-name': share_name,
833+ 'access': permission}
834+ if name != 'admin@local':
835+ share_list[name].pop('display-name')
836+ else:
837+ share_list[name]['access'] = 'admin'
838+ return share_list
839+
840 def _log_command(self, command, args, model, level=logging.INFO):
841 full_args = ['juju', command]
842 if model is not None:
843@@ -566,6 +612,16 @@
844 parser.add_argument('model_name')
845 parsed = parser.parse_args(args)
846 self.controller_state.add_model(parsed.model_name)
847+ if command == 'revoke':
848+ user_name = args[2]
849+ permissions = args[5]
850+ per = self.controller_state.users[user_name]['permission']
851+ if per == permissions:
852+ if permissions == 'read':
853+ self.controller_state.shares.remove(user_name)
854+ per = ''
855+ else:
856+ per = 'read'
857
858 @contextmanager
859 def juju_async(self, command, args, used_feature_flags,
860@@ -576,7 +632,7 @@
861
862 @check_juju_output
863 def get_juju_output(self, command, args, used_feature_flags,
864- juju_home, model=None, timeout=None):
865+ juju_home, model=None, timeout=None, user_name=None):
866 if 'service' in command:
867 raise Exception('No service')
868 self._log_command(command, args, model, logging.DEBUG)
869@@ -597,6 +653,12 @@
870 return yaml.safe_dump(self.make_controller_dict(args[0]))
871 if command == 'list-models':
872 return yaml.safe_dump(self.list_models())
873+ if command == 'list-users':
874+ return json.dumps(self.list_users())
875+ if command == 'list-shares':
876+ return json.dumps(self.list_shares())
877+ if command == 'show-user':
878+ return json.dumps(self.show_user(user_name))
879 if command == 'add-user':
880 permissions = 'read'
881 if set(["--acl", "write"]).issubset(args):
882@@ -606,6 +668,9 @@
883 info_string = \
884 'User "{}" added\nUser "{}"granted {} access to model "{}\n"' \
885 .format(username, username, permissions, model)
886+ self.controller_state.users.update(
887+ {username: {'state': '', 'permission': permissions}})
888+ self.controller_state.shares.append(username)
889 register_string = get_user_register_command_info(username)
890 return info_string + register_string
891 if command == 'show-status':
892@@ -3045,7 +3110,8 @@
893 gjo_mock.assert_called_once_with(
894 'run', ('--format', 'json', '--application', 'foo,bar', 'wname'),
895 frozenset(
896- ['address-allocation', 'migration']), 'foo', 'name:name', None)
897+ ['address-allocation', 'migration']), 'foo',
898+ 'name:name', None, user_name=None)
899
900 def test_list_space(self):
901 client = EnvJujuClient(JujuData(None, {'type': 'local'}),
902@@ -3248,6 +3314,46 @@
903 get_output.assert_called_with(
904 'add-user', *expected_args, include_e=False)
905
906+ def test_disable_user(self):
907+ env = JujuData('foo')
908+ username = 'fakeuser'
909+ client = EnvJujuClient(env, None, None)
910+ with patch.object(client, 'juju') as mock:
911+ client.disable_user(username)
912+ mock.assert_called_with(
913+ 'disable-user', ('-c', 'foo', 'fakeuser'), include_e=False)
914+
915+ def test_enable_user(self):
916+ env = JujuData('foo')
917+ username = 'fakeuser'
918+ client = EnvJujuClient(env, None, None)
919+ with patch.object(client, 'juju') as mock:
920+ client.enable_user(username)
921+ mock.assert_called_with(
922+ 'enable-user', ('-c', 'foo', 'fakeuser'), include_e=False)
923+
924+ def test_logout(self):
925+ env = JujuData('foo')
926+ client = EnvJujuClient(env, None, None)
927+ with patch.object(client, 'juju') as mock:
928+ client.logout()
929+ mock.assert_called_with(
930+ 'logout', ('-c', 'foo'), include_e=False)
931+
932+ def test_create_cloned_environment(self):
933+ fake_client = fake_juju_client()
934+ fake_client.bootstrap()
935+ # fake_client_environ = fake_client._shell_environ()
936+ controller_name = 'user_controller'
937+ cloned = fake_client.create_cloned_environment(
938+ 'fakehome',
939+ controller_name
940+ )
941+ self.assertIs(fake_client.__class__, type(cloned))
942+ self.assertEqual(cloned.env.juju_home, 'fakehome')
943+ self.assertEqual(cloned.env.controller.name, controller_name)
944+ self.assertEqual(fake_client.env.controller.name, 'name')
945+
946
947 class TestEnvJujuClient2B8(ClientTest):
948
949@@ -5070,7 +5176,8 @@
950 gjo_mock.assert_called_once_with(
951 'run', ('--format', 'json', '--service', 'foo,bar', 'wname'),
952 frozenset(
953- ['address-allocation', 'migration']), 'foo', 'name', None)
954+ ['address-allocation', 'migration']),
955+ 'foo', 'name', None, user_name=None)
956
957 def test_list_space(self):
958 client = EnvJujuClient1X(SimpleEnvironment(None, {'type': 'local'}),

Subscribers

People subscribed via source and target branches