blob: 93e2883b42e4e7e8a16b447a1f841ad2cf64a62f [file] [log] [blame]
Adam Israel1a15d1c2017-10-23 12:00:49 -04001import asyncio
Adam Israelb0943662018-08-02 15:32:00 -04002import subprocess
Adam Israeldcdf82b2017-08-15 15:26:43 -04003import uuid
4
Adam Israelc3e6c2e2018-03-01 09:31:50 -05005from juju.client.connection import Connection
Adam Israelb0943662018-08-02 15:32:00 -04006from juju.client.jujudata import FileJujuData
7from juju.controller import Controller
Adam Israeldcdf82b2017-08-15 15:26:43 -04008from juju.errors import JujuAPIError
9
Adam Israelc3e6c2e2018-03-01 09:31:50 -050010import pytest
11
12from .. import base
13
Adam Israeldcdf82b2017-08-15 15:26:43 -040014
15@base.bootstrapped
16@pytest.mark.asyncio
Adam Israel1a15d1c2017-10-23 12:00:49 -040017async def test_add_remove_user(event_loop):
Adam Israeldcdf82b2017-08-15 15:26:43 -040018 async with base.CleanController() as controller:
19 username = 'test{}'.format(uuid.uuid4())
Adam Israel1a15d1c2017-10-23 12:00:49 -040020 user = await controller.get_user(username)
21 assert user is None
22 user = await controller.add_user(username)
23 assert user is not None
24 assert user.username == username
25 users = await controller.get_users()
26 assert any(u.username == username for u in users)
27 await controller.remove_user(username)
28 user = await controller.get_user(username)
29 assert user is None
30 users = await controller.get_users()
31 assert not any(u.username == username for u in users)
Adam Israeldcdf82b2017-08-15 15:26:43 -040032
33
34@base.bootstrapped
35@pytest.mark.asyncio
36async def test_disable_enable_user(event_loop):
37 async with base.CleanController() as controller:
38 username = 'test-disable{}'.format(uuid.uuid4())
Adam Israel1a15d1c2017-10-23 12:00:49 -040039 user = await controller.add_user(username)
40
41 await user.disable()
42 assert not user.enabled
43 assert user.disabled
44
45 fresh = await controller.get_user(username) # fetch fresh copy
46 assert not fresh.enabled
47 assert fresh.disabled
48
49 await user.enable()
50 assert user.enabled
51 assert not user.disabled
52
53 fresh = await controller.get_user(username) # fetch fresh copy
54 assert fresh.enabled
55 assert not fresh.disabled
Adam Israeldcdf82b2017-08-15 15:26:43 -040056
57
58@base.bootstrapped
59@pytest.mark.asyncio
60async def test_change_user_password(event_loop):
61 async with base.CleanController() as controller:
62 username = 'test-password{}'.format(uuid.uuid4())
Adam Israel1a15d1c2017-10-23 12:00:49 -040063 user = await controller.add_user(username)
64 await user.set_password('password')
Adam Israelc3e6c2e2018-03-01 09:31:50 -050065 # Check that we can connect with the new password.
66 new_connection = None
Adam Israeldcdf82b2017-08-15 15:26:43 -040067 try:
Adam Israelc3e6c2e2018-03-01 09:31:50 -050068 kwargs = controller.connection().connect_params()
69 kwargs['username'] = username
70 kwargs['password'] = 'password'
71 new_connection = await Connection.connect(**kwargs)
Adam Israeldcdf82b2017-08-15 15:26:43 -040072 except JujuAPIError:
Adam Israel1a15d1c2017-10-23 12:00:49 -040073 raise AssertionError('Unable to connect with new password')
74 finally:
Adam Israelc3e6c2e2018-03-01 09:31:50 -050075 if new_connection:
76 await new_connection.close()
Adam Israeldcdf82b2017-08-15 15:26:43 -040077
78
79@base.bootstrapped
80@pytest.mark.asyncio
Adam Israel1a15d1c2017-10-23 12:00:49 -040081async def test_grant_revoke(event_loop):
Adam Israeldcdf82b2017-08-15 15:26:43 -040082 async with base.CleanController() as controller:
83 username = 'test-grant{}'.format(uuid.uuid4())
Adam Israel1a15d1c2017-10-23 12:00:49 -040084 user = await controller.add_user(username)
85 await user.grant('superuser')
86 assert user.access == 'superuser'
87 fresh = await controller.get_user(username) # fetch fresh copy
88 assert fresh.access == 'superuser'
Adam Israelc3e6c2e2018-03-01 09:31:50 -050089 await user.grant('login') # already has 'superuser', so no-op
90 assert user.access == 'superuser'
Adam Israel1a15d1c2017-10-23 12:00:49 -040091 fresh = await controller.get_user(username) # fetch fresh copy
Adam Israelc3e6c2e2018-03-01 09:31:50 -050092 assert fresh.access == 'superuser'
Adam Israel1a15d1c2017-10-23 12:00:49 -040093 await user.revoke()
94 assert user.access is ''
95 fresh = await controller.get_user(username) # fetch fresh copy
96 assert fresh.access is ''
Adam Israeldcdf82b2017-08-15 15:26:43 -040097
98
99@base.bootstrapped
100@pytest.mark.asyncio
Adam Israel1a15d1c2017-10-23 12:00:49 -0400101async def test_list_models(event_loop):
Adam Israeldcdf82b2017-08-15 15:26:43 -0400102 async with base.CleanController() as controller:
Adam Israel1a15d1c2017-10-23 12:00:49 -0400103 async with base.CleanModel() as model:
104 result = await controller.list_models()
105 assert model.info.name in result
106
107
108@base.bootstrapped
109@pytest.mark.asyncio
110async def test_get_model(event_loop):
111 async with base.CleanController() as controller:
112 by_name, by_uuid = None, None
113 model_name = 'test-{}'.format(uuid.uuid4())
114 model = await controller.add_model(model_name)
115 model_uuid = model.info.uuid
116 await model.disconnect()
117 try:
118 by_name = await controller.get_model(model_name)
119 by_uuid = await controller.get_model(model_uuid)
120 assert by_name.info.name == model_name
121 assert by_name.info.uuid == model_uuid
122 assert by_uuid.info.name == model_name
123 assert by_uuid.info.uuid == model_uuid
124 finally:
125 if by_name:
126 await by_name.disconnect()
127 if by_uuid:
128 await by_uuid.disconnect()
129 await controller.destroy_model(model_name)
130
131
Adam Israelc3e6c2e2018-03-01 09:31:50 -0500132async def _wait_for_model(controller, model_name):
133 while model_name not in await controller.list_models():
134 await asyncio.sleep(0.5, loop=controller.loop)
135
136
Adam Israel1a15d1c2017-10-23 12:00:49 -0400137async def _wait_for_model_gone(controller, model_name):
138 while model_name in await controller.list_models():
139 await asyncio.sleep(0.5, loop=controller.loop)
140
141
142@base.bootstrapped
143@pytest.mark.asyncio
144async def test_destroy_model_by_name(event_loop):
145 async with base.CleanController() as controller:
146 model_name = 'test-{}'.format(uuid.uuid4())
147 model = await controller.add_model(model_name)
148 await model.disconnect()
Adam Israelc3e6c2e2018-03-01 09:31:50 -0500149 await asyncio.wait_for(_wait_for_model(controller,
150 model_name),
151 timeout=60)
Adam Israel1a15d1c2017-10-23 12:00:49 -0400152 await controller.destroy_model(model_name)
153 await asyncio.wait_for(_wait_for_model_gone(controller,
154 model_name),
155 timeout=60)
156
157
158@base.bootstrapped
159@pytest.mark.asyncio
160async def test_add_destroy_model_by_uuid(event_loop):
161 async with base.CleanController() as controller:
162 model_name = 'test-{}'.format(uuid.uuid4())
163 model = await controller.add_model(model_name)
164 model_uuid = model.info.uuid
165 await model.disconnect()
Adam Israelc3e6c2e2018-03-01 09:31:50 -0500166 await asyncio.wait_for(_wait_for_model(controller,
167 model_name),
168 timeout=60)
Adam Israel1a15d1c2017-10-23 12:00:49 -0400169 await controller.destroy_model(model_uuid)
170 await asyncio.wait_for(_wait_for_model_gone(controller,
171 model_name),
172 timeout=60)
Adam Israelb0943662018-08-02 15:32:00 -0400173
174
175# this test must be run serially because it modifies the login password
176@pytest.mark.serial
177@base.bootstrapped
178@pytest.mark.asyncio
179async def test_macaroon_auth(event_loop):
180 jujudata = FileJujuData()
181 account = jujudata.accounts()[jujudata.current_controller()]
182 with base.patch_file('~/.local/share/juju/accounts.yaml'):
183 if 'password' in account:
184 # force macaroon auth by "changing" password to current password
185 result = subprocess.run(
186 ['juju', 'change-user-password'],
187 input='{0}\n{0}\n'.format(account['password']),
188 universal_newlines=True,
189 stderr=subprocess.PIPE)
190 assert result.returncode == 0, ('Failed to change password: '
191 '{}'.format(result.stderr))
192 controller = Controller()
193 try:
194 await controller.connect()
195 assert controller.is_connected()
196 finally:
197 if controller.is_connected():
198 await controller.disconnect()
199 async with base.CleanModel():
200 pass # create and login to model works