Skip to content

Commit 3c31d0a

Browse files
authored
Merge pull request #697 from cderici/fix-no-model-name-case
#697 #### Description This PR fixes the `model.connect_current()` call which currently fails at the after_connect where we pull up the model info. The reason of the failure is that we neither have a `model_name`, nor a `model_uuid` for the controller to pull up the info. To solve it, this change gets the `model_uuid` from the connector during connect (if we don't use the endpoints, in which case we don't have a problem to begin with). #### QA Steps This change also introduces an integration test, namely, `test_connect_current`, so the following should pass. ``` tox -e integration -- tests/integration/test_model.py::test_connect_current ``` #### Notes & Discussion
2 parents 80eb84e + 3b22916 commit 3c31d0a

8 files changed

Lines changed: 39 additions & 21 deletions

File tree

examples/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ async def main():
3737
# update and check app config
3838
await ubuntu_app.set_config({'tuning-level': 'fast'})
3939
config = await ubuntu_app.get_config()
40-
assert(config['tuning-level']['value'] == 'fast')
40+
assert (config['tuning-level']['value'] == 'fast')
4141

4242
# update and check app constraints
4343
await ubuntu_app.set_constraints({'mem': 512 * MB})
4444
constraints = await ubuntu_app.get_constraints()
45-
assert(constraints['mem'] == 512 * MB)
45+
assert (constraints['mem'] == 512 * MB)
4646

4747
await model.disconnect()
4848

examples/relate.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
class MyRemoveObserver(ModelObserver):
1818
async def on_change(self, delta, old, new, model):
1919
if delta.type == 'remove':
20-
assert(new.latest() == new)
21-
assert(new.next() is None)
22-
assert(new.dead)
23-
assert(new.current)
24-
assert(new.connected)
25-
assert(new.previous().dead)
26-
assert(not new.previous().current)
27-
assert(not new.previous().connected)
20+
assert (new.latest() == new)
21+
assert (new.next() is None)
22+
assert (new.dead)
23+
assert (new.current)
24+
assert (new.connected)
25+
assert (new.previous().dead)
26+
assert (not new.previous().current)
27+
assert (not new.previous().connected)
2828

2929

3030
class MyModelObserver(ModelObserver):

juju/client/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ async def rpc(self, msg, encoder=None):
595595
'''
596596
self.__request_id__ += 1
597597
msg['request-id'] = self.__request_id__
598-
if'params' not in msg:
598+
if 'params' not in msg:
599599
msg['params'] = {}
600600
if "version" not in msg:
601601
msg['version'] = self.facades[msg['type']]

juju/client/connector.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ async def connect_controller(self, controller_name=None, specified_facades=None)
113113
self.controller_name = controller_name
114114
self.controller_uuid = controller["uuid"]
115115

116-
async def connect_model(self, model_name=None, **kwargs):
116+
async def connect_model(self, _model_name=None, **kwargs):
117117
"""Connect to a model by name. If either controller or model
118118
parts of the name are empty, the current controller and/or model
119119
will be used.
@@ -122,7 +122,7 @@ async def connect_model(self, model_name=None, **kwargs):
122122
"""
123123

124124
try:
125-
controller_name, model_name = self.jujudata.parse_model(model_name)
125+
controller_name, _model_name = self.jujudata.parse_model(_model_name)
126126
controller = self.jujudata.controllers().get(controller_name)
127127
except JujuError as e:
128128
raise JujuConnectionError(e.message) from e
@@ -134,8 +134,8 @@ async def connect_model(self, model_name=None, **kwargs):
134134
models = self.jujudata.models().get(controller_name, {}).get('models',
135135
{})
136136
model_uuid = None
137-
if model_name in models:
138-
model_uuid = models[model_name]['uuid']
137+
if _model_name in models:
138+
model_uuid = models[_model_name]['uuid']
139139
else:
140140
# let's try to find it through the actual controller
141141
await self.connect_controller(controller_name=controller_name)
@@ -146,11 +146,11 @@ async def connect_model(self, model_name=None, **kwargs):
146146
response = await controller_facade.AllModels()
147147
# search the one that contains admin/model_name
148148
for user_model in response.user_models:
149-
if 'admin/' + user_model.model.name == model_name:
149+
if 'admin/' + user_model.model.name == _model_name:
150150
model_uuid = user_model.model.uuid
151151

152152
if model_uuid is None:
153-
raise JujuConnectionError('Model not found: {}'.format(model_name))
153+
raise JujuConnectionError('Model not found: {}'.format(_model_name))
154154

155155
proxy = proxy_from_config(controller.get('proxy-config', None))
156156

@@ -167,7 +167,8 @@ async def connect_model(self, model_name=None, **kwargs):
167167
# TODO this might be a good spot to trigger refreshing the
168168
# local cache (the connection to the model might help)
169169
self.controller_name = controller_name
170-
self.model_name = controller_name + ':' + model_name
170+
self.model_name = controller_name + ':' + _model_name
171+
return self.controller_name, model_uuid
171172

172173
def bakery_client_for_controller(self, controller_name):
173174
'''Make a copy of the bakery client with a the appropriate controller's

juju/controller.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,10 @@ async def get_model_info(self, model_name=None, model_uuid=None):
499499
facade = client.ModelManagerFacade.from_connection(self.connection())
500500
if model_uuid is None:
501501
uuids = await self.model_uuids()
502-
model_uuid = uuids[model_name]
502+
try:
503+
model_uuid = uuids[model_name]
504+
except KeyError:
505+
raise errors.JujuError("{} is not among the models in the controller : {}".format(model_name, uuids))
503506
entity = client.Entity(tag.model(model_uuid))
504507
_model_info_results = await facade.ModelInfo(entities=[entity])
505508
return _model_info_results.results[0].result

juju/model.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,14 +690,16 @@ async def connect(self, *args, **kwargs):
690690
await self.disconnect()
691691
model_name = model_uuid = None
692692
if 'endpoint' not in kwargs and len(args) < 2:
693+
# Then we're using the model_name to pick the model
693694
if args and 'model_name' in kwargs:
694695
raise TypeError('connect() got multiple values for model_name')
695696
elif args:
696697
model_name = args[0]
697698
else:
698699
model_name = kwargs.pop('model_name', None)
699-
await self._connector.connect_model(model_name, **kwargs)
700+
_, model_uuid = await self._connector.connect_model(model_name, **kwargs)
700701
else:
702+
# Then we're using the endpoint to pick the model
701703
if 'model_name' in kwargs:
702704
raise TypeError('connect() got values for both '
703705
'model_name and endpoint')

tests/integration/test_model.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ async def add_manual_machine_ssh(event_loop, is_root=False):
445445
def wait_for_network(container, timeout=30):
446446
"""Wait for eth0 to have an ipv4 address."""
447447
starttime = time.time()
448-
while(time.time() < starttime + timeout):
448+
while time.time() < starttime + timeout:
449449
time.sleep(1)
450450
if 'eth0' in container.state().network:
451451
addresses = container.state().network['eth0']['addresses']
@@ -1012,6 +1012,14 @@ async def test_connect_to_connection(event_loop):
10121012
await m.disconnect()
10131013

10141014

1015+
@base.bootstrapped
1016+
@pytest.mark.asyncio
1017+
async def test_connect_current(event_loop):
1018+
m = Model()
1019+
await m.connect_current()
1020+
assert m.is_connected()
1021+
1022+
10151023
@base.bootstrapped
10161024
@pytest.mark.asyncio
10171025
async def test_model_cache_update(event_loop):

tests/unit/test_model.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,20 +170,23 @@ class TestModelConnect(asynctest.TestCase):
170170
@pytest.mark.asyncio
171171
async def test_no_args(self, mock_connect_model, _):
172172
m = Model()
173+
mock_connect_model.side_effect = [("_", "uuid")]
173174
await m.connect()
174175
mock_connect_model.assert_called_once_with(None)
175176

176177
@asynctest.patch('juju.client.connector.Connector.connect_model')
177178
@pytest.mark.asyncio
178179
async def test_with_model_name(self, mock_connect_model, _):
179180
m = Model()
181+
mock_connect_model.side_effect = [("_", "uuid")]
180182
await m.connect(model_name='foo')
181183
mock_connect_model.assert_called_once_with('foo')
182184

183185
@asynctest.patch('juju.client.connector.Connector.connect_model')
184186
@pytest.mark.asyncio
185187
async def test_with_endpoint_but_no_uuid(self, mock_connect_model, _):
186188
m = Model()
189+
mock_connect_model.side_effect = [("_", "uuid")]
187190
with self.assertRaises(TypeError):
188191
await m.connect(endpoint='0.1.2.3:4566')
189192
self.assertEqual(mock_connect_model.call_count, 0)
@@ -251,6 +254,7 @@ async def test_with_endpoint_and_uuid_with_macaroon(self, mock_connect, _):
251254
@asynctest.patch('juju.client.connector.Connector.connect')
252255
async def test_with_posargs(self, mock_connect, mock_connect_model, _):
253256
m = Model()
257+
mock_connect_model.side_effect = [("_", "uuid")]
254258
await m.connect('foo')
255259
mock_connect_model.assert_called_once_with('foo')
256260
with self.assertRaises(TypeError):

0 commit comments

Comments
 (0)