Skip to content

Commit 6aa065e

Browse files
authored
Merge pull request #984 from cderici/controller-name-lazy-jujudata
#984 #### Description This moves the computation of the `controller_name` from the `connector` to the `Controller` class, to be lazily evaluated from `jujudata` (and cached). We can move it out of the connector safely because: 1 - `controller_name` is not used within the connector. 2 - the only location that the `controller_name` is returned to, ignores it. This has the nice consequence of freeing pylibjuju from relying on things like the `controllers.yaml` file (in turn the juju client to be installed) to connect to a controller. Fixes #983 #### QA Steps Added a test for lazy computation of `controller_name`. ```sh $ tox -e integration -- tests/integration/test_controller.py::test_connection_lazy_jujudata ``` But the scenario from the #983 can be tried easily as follows: ```python # make a new controller and connect it in a normal way: from juju import controller c = controller.Controller() await c.connect() # get the connection conn = c.connection() ``` Now go move the `.local/share/juju/controllers.yaml` file temporarily. ```python # make a new controller c2 = controller.Controller() # connect it using the information within the connection we got above await c2.connect(endpoint=conn.endpoints[0][0], cacert=conn.cacert, username=conn.usertag, password=conn.password) ``` Should succeed without any errors. Don't forget to move back your `controllers.yaml` back so nothing else on your machine freaks out. All CI tests need to pass. #### Notes & Discussion juju-4891
2 parents 70dda82 + 1868e1e commit 6aa065e

4 files changed

Lines changed: 28 additions & 13 deletions

File tree

juju/client/connector.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from juju.client.gocookies import GoCookieJar, go_to_py_cookie
1010
from juju.client.jujudata import FileJujuData, API_ENDPOINTS_KEY
1111
from juju.client.proxy.factory import proxy_from_config
12-
from juju.errors import JujuConnectionError, JujuError, PylibjujuProgrammingError
12+
from juju.errors import JujuConnectionError, JujuError
1313
from juju.client import client
1414
from juju.version import SUPPORTED_MAJOR_VERSION, TARGET_JUJU_VERSION
1515

@@ -38,7 +38,6 @@ def __init__(
3838
self.bakery_client = bakery_client
3939
self._connection = None
4040
self._log_connection = None
41-
self.controller_name = None
4241
self.controller_uuid = None
4342
self.model_name = None
4443
self.jujudata = jujudata or FileJujuData()
@@ -83,11 +82,6 @@ async def connect(self, **kwargs):
8382
await self._connection.close()
8483
self._connection = await Connection.connect(**kwargs)
8584

86-
if not self.controller_name:
87-
if 'endpoint' not in kwargs:
88-
raise PylibjujuProgrammingError("Please report this error to the maintainers.")
89-
self.controller_name = self.jujudata.controller_name_by_endpoint(kwargs['endpoint'])
90-
9185
# Check if we support the target controller
9286
juju_server_version = self._connection.info['server-version']
9387
if not juju_server_version.startswith(TARGET_JUJU_VERSION):
@@ -132,7 +126,6 @@ async def connect_controller(self, controller_name=None, specified_facades=None)
132126
specified_facades=specified_facades,
133127
proxy=proxy,
134128
)
135-
self.controller_name = controller_name
136129
self.controller_uuid = controller["uuid"]
137130

138131
async def connect_model(self, _model_name=None, **kwargs):
@@ -188,9 +181,8 @@ async def connect_model(self, _model_name=None, **kwargs):
188181
await self.connect(**kwargs)
189182
# TODO this might be a good spot to trigger refreshing the
190183
# local cache (the connection to the model might help)
191-
self.controller_name = controller_name
192184
self.model_name = controller_name + ':' + _model_name
193-
return self.controller_name, model_uuid
185+
return model_uuid
194186

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

juju/controller.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ def __init__(
4949
bakery_client=bakery_client,
5050
jujudata=jujudata,
5151
)
52+
self._controller_name = None
5253

5354
async def __aenter__(self):
5455
await self.connect()
@@ -174,7 +175,13 @@ def connection(self):
174175

175176
@property
176177
def controller_name(self):
177-
return self._connector.controller_name
178+
if not self._controller_name:
179+
try:
180+
self._controller_name = self._connector.jujudata.controller_name_by_endpoint(
181+
self._connector.connection().endpoint)
182+
except FileNotFoundError:
183+
raise errors.PylibjujuError("Unable to determine controller name. controllers.yaml not found.")
184+
return self._controller_name
178185

179186
@property
180187
def controller_uuid(self):

juju/model.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ async def connect(self, *args, **kwargs):
663663
model_name = args[0]
664664
else:
665665
model_name = kwargs.pop('model_name', None)
666-
_, model_uuid = await self._connector.connect_model(model_name, **kwargs)
666+
model_uuid = await self._connector.connect_model(model_name, **kwargs)
667667
else:
668668
# Then we're using the endpoint to pick the model
669669
if 'model_name' in kwargs:

tests/integration/test_controller.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import uuid
66
import hvac
77

8-
from juju import access
8+
from juju import access, controller
99
from juju.client.connection import Connection
1010
from juju.client import client
1111
from juju.errors import JujuAPIError, JujuError
@@ -298,6 +298,22 @@ async def test_grant_revoke_controller_access(event_loop):
298298
raise
299299

300300

301+
@base.bootstrapped
302+
async def test_connection_lazy_jujudata(event_loop):
303+
async with base.CleanController() as cont1:
304+
conn = cont1.connection()
305+
new_controller = controller.Controller()
306+
await new_controller.connect(endpoint=conn.endpoints[0][0],
307+
cacert=conn.cacert,
308+
username=conn.usertag,
309+
password=conn.password,
310+
)
311+
assert new_controller._controller_name is None
312+
new_controller.controller_name
313+
assert new_controller._controller_name is not None
314+
await new_controller.disconnect()
315+
316+
301317
@base.bootstrapped
302318
async def test_grant_revoke_model_access(event_loop):
303319
async with base.CleanController() as controller:

0 commit comments

Comments
 (0)