Merge lp:~abentley/juju-ci-tools/client-for-existing into lp:juju-ci-tools

Proposed by Aaron Bentley
Status: Merged
Merged at revision: 1906
Proposed branch: lp:~abentley/juju-ci-tools/client-for-existing
Merge into: lp:juju-ci-tools
Diff against target: 194 lines (+89/-4)
6 files modified
jujupy/__init__.py (+2/-0)
jujupy/client.py (+8/-0)
jujupy/fake.py (+5/-0)
jujupy/tests/test_client.py (+10/-0)
jujupy/tests/test_version_client.py (+24/-0)
jujupy/version_client.py (+40/-4)
To merge this branch: bzr merge lp:~abentley/juju-ci-tools/client-for-existing
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+318146@code.launchpad.net

Commit message

Add client_for_existing.

Description of the change

This branch provides client_for_existing.

This can be used to instantiate a ModelClient for a specific JUJU_DATA directory. It autodetects the existing model and controller names. In this version, they cannot be overridden, but the usual clone methods can be used to change model_name.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you thank you, thank you. I think the migration and user tests might use this.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'jujupy/__init__.py'
2--- jujupy/__init__.py 2017-02-22 16:40:34 +0000
3+++ jujupy/__init__.py 2017-02-23 19:09:25 +0000
4@@ -40,6 +40,7 @@
5 )
6 from jujupy.version_client import (
7 client_from_config,
8+ client_for_existing,
9 EnvJujuClient1X,
10 EnvJujuClient25,
11 get_client_class,
12@@ -50,6 +51,7 @@
13 'AgentsNotStarted',
14 'AuthNotAccepted',
15 'client_from_config',
16+ 'client_for_existing',
17 'ConditionList',
18 'EnvJujuClient1X',
19 'EnvJujuClient25',
20
21=== modified file 'jujupy/client.py'
22--- jujupy/client.py 2017-02-22 22:04:51 +0000
23+++ jujupy/client.py 2017-02-23 19:09:25 +0000
24@@ -1232,6 +1232,14 @@
25 raise e
26 return sub_output
27
28+ def get_active_model(self, juju_data_dir):
29+ """Determine the active model in a juju data dir."""
30+ current = self.get_juju_output(
31+ 'switch', (), set(), juju_data_dir, model=None).decode('ascii')
32+ controller_name, user_model = current.split(':', 1)
33+ user_name, model_name = user_model.split('/', 1)
34+ return controller_name, user_name, model_name.rstrip('\n')
35+
36 def pause(self, seconds):
37 pause(seconds)
38
39
40=== modified file 'jujupy/fake.py'
41--- jujupy/fake.py 2017-02-22 19:05:46 +0000
42+++ jujupy/fake.py 2017-02-23 19:09:25 +0000
43@@ -59,6 +59,7 @@
44 }
45 }
46 self.shares = ['admin']
47+ self.active_model = None
48
49 def add_model(self, name):
50 state = FakeEnvironmentState(self)
51@@ -640,6 +641,10 @@
52 if self._past_deadline and not self._ignore_soft_deadline:
53 raise SoftDeadlineExceeded()
54
55+ def get_active_model(self, juju_home):
56+ return (self.controller_state.name, None,
57+ self.controller_state.active_model)
58+
59 def deploy(self, model_state, charm_name, num, service_name=None,
60 series=None):
61 if service_name is None:
62
63=== modified file 'jujupy/tests/test_client.py'
64--- jujupy/tests/test_client.py 2017-02-22 22:04:51 +0000
65+++ jujupy/tests/test_client.py 2017-02-23 19:09:25 +0000
66@@ -295,6 +295,16 @@
67 'Operation exceeded deadline.'):
68 backend.get_juju_output('cmd', ('args',), [], 'home')
69
70+ def test_get_active_model(self):
71+ backend = Juju2Backend('/bin/path', '2.0', set(), debug=False,
72+ soft_deadline=None)
73+ with patch('subprocess.Popen') as mock_popen:
74+ mock_popen.return_value.communicate.return_value = (
75+ 'ctrl1:user1/model1\n', '')
76+ mock_popen.return_value.returncode = 0
77+ result = backend.get_active_model('/foo/bar')
78+ self.assertEqual(('ctrl1', 'user1', 'model1'), result)
79+
80
81 class TestBaseCondition(ClientTest):
82
83
84=== modified file 'jujupy/tests/test_version_client.py'
85--- jujupy/tests/test_version_client.py 2017-02-22 18:02:19 +0000
86+++ jujupy/tests/test_version_client.py 2017-02-23 19:09:25 +0000
87@@ -42,15 +42,19 @@
88 )
89 from jujupy.configuration import get_jenv_path
90 from jujupy.fake import (
91+ FakeBackend,
92+ FakeControllerState,
93 FakeBackend2_1,
94 )
95 from jujupy.version_client import (
96 BootstrapMismatch,
97+ client_for_existing,
98 client_from_config,
99 EnvJujuClient1X,
100 EnvJujuClient22,
101 EnvJujuClient24,
102 EnvJujuClient25,
103+ get_client_class,
104 ModelClientRC,
105 IncompatibleConfigClass,
106 Juju1XBackend,
107@@ -214,6 +218,26 @@
108 self.assertEqual(client._backend.soft_deadline, deadline)
109
110
111+class TestClientForExisting(ClientTest):
112+
113+ def test_client_for_existing(self):
114+ state = FakeControllerState()
115+ state.name = 'ctrl1'
116+ state.active_model = 'model1'
117+ backend = FakeBackend(state, version='2.0.73', full_path='/path/juju')
118+ with patch.object(ModelClient, 'get_version',
119+ return_value=backend.version) as gv_mock:
120+ client_class = get_client_class(backend.version)
121+ with patch.object(client_class, 'default_backend',
122+ return_value=backend) as db_mock:
123+ client = client_for_existing(backend.full_path, '/juju-data')
124+ self.assertEqual(client.model_name, 'model1')
125+ self.assertEqual(client.env.controller.name, 'ctrl1')
126+ gv_mock.assert_called_once_with(backend.full_path)
127+ db_mock.assert_called_once_with(backend.full_path, backend.version,
128+ set(), debug=False, soft_deadline=None)
129+
130+
131 class TestModelClient2_1(ClientTest):
132
133 client_version = '2.1.0'
134
135=== modified file 'jujupy/version_client.py'
136--- jujupy/version_client.py 2017-02-22 19:49:58 +0000
137+++ jujupy/version_client.py 2017-02-23 19:09:25 +0000
138@@ -701,6 +701,18 @@
139 return client_class
140
141
142+def get_full_path(juju_path):
143+ """Helper to ensure a full path is used.
144+
145+ If juju_path is None, ModelClient.get_full_path is used. Otherwise,
146+ the supplied path is converted to absolute.
147+ """
148+ if juju_path is None:
149+ return ModelClient.get_full_path()
150+ else:
151+ return os.path.abspath(juju_path)
152+
153+
154 def client_from_config(config, juju_path, debug=False, soft_deadline=None):
155 """Create a client from an environment's configuration.
156
157@@ -717,9 +729,33 @@
158 env = client_class.config_class('', {})
159 else:
160 env = client_class.config_class.from_config(config)
161- if juju_path is None:
162- full_path = ModelClient.get_full_path()
163- else:
164- full_path = os.path.abspath(juju_path)
165+ full_path = get_full_path(juju_path)
166 return client_class(env, version, full_path, debug=debug,
167 soft_deadline=soft_deadline)
168+
169+
170+def client_for_existing(juju_path, juju_data_dir, debug=False,
171+ soft_deadline=None):
172+ """Create a client for an existing controller/model.
173+
174+ :param juju_path: Path to juju binary the client should wrap.
175+ :param juju_data_dir: Path to the juju data directory referring the the
176+ controller and model.
177+ :param debug=False: The debug flag for the client, False by default.
178+ :param soft_deadline: A datetime representing the deadline by which
179+ normal operations should complete. If None, no deadline is
180+ enforced.
181+ """
182+ version = ModelClient.get_version(juju_path)
183+ client_class = get_client_class(str(version))
184+ full_path = get_full_path(juju_path)
185+ backend = client_class.default_backend(full_path, version, set(),
186+ debug=debug,
187+ soft_deadline=soft_deadline)
188+ controller_name, user_name, model_name = backend.get_active_model(
189+ juju_data_dir)
190+ config = client_class.config_class(
191+ model_name, controller=Controller(controller_name),
192+ juju_home=juju_data_dir)
193+ return client_class(config, version, full_path, debug=debug,
194+ soft_deadline=soft_deadline, _backend=backend)

Subscribers

People subscribed via source and target branches