Merge lp:~frankban/charms/precise/juju-gui/new-darwin into lp:~juju-gui/charms/precise/juju-gui/trunk
- Precise Pangolin (12.04)
- new-darwin
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 109 |
Proposed branch: | lp:~frankban/charms/precise/juju-gui/new-darwin |
Merge into: | lp:~juju-gui/charms/precise/juju-gui/trunk |
Diff against target: |
681 lines (+37/-468) 11 files modified
hooks/utils.py (+1/-1) revision (+1/-1) server/guiserver/__init__.py (+1/-1) server/guiserver/bundles/__init__.py (+14/-13) server/guiserver/bundles/base.py (+15/-2) server/guiserver/bundles/blocking.py (+0/-143) server/guiserver/tests/bundles/test_base.py (+2/-1) server/guiserver/tests/bundles/test_blocking.py (+0/-189) server/guiserver/tests/helpers.py (+3/-49) server/guiserver/tests/test_utils.py (+0/-51) server/guiserver/utils.py (+0/-17) |
To merge this branch: | bzr merge lp:~frankban/charms/precise/juju-gui/new-darwin |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
charmers | Pending | ||
Review via email: mp+187015@code.launchpad.net |
Commit message
Description of the change
New juju-deployer darwin version.
Switched to the new juju-deployer version,
which includes support for deployments started
by the GUI server.
This allows us to remove the blocking/
code from the GUI server.
Also updated the relevant parts of the documentation.
Tests: run `make unittest` from the root of this branch.
Francesco Banconi (frankban) wrote : | # |
Gary Poster (gary) wrote : | # |
LGTM. No qa, guessing bac is doing it. Thank you for getting this into
the deployer and connecting with Kapil!
Brad Crittenden (bac) wrote : | # |
LGTM with minor typo. Ran the tests but after talking to Francesco did
not do QA.
https:/
File server/
https:/
server/
in the juju-deployer library is responsible of
s/of/for
- 118. By Francesco Banconi
-
Changes as per review.
Francesco Banconi (frankban) wrote : | # |
*** Submitted:
New juju-deployer darwin version.
Switched to the new juju-deployer version,
which includes support for deployments started
by the GUI server.
This allows us to remove the blocking/
code from the GUI server.
Also updated the relevant parts of the documentation.
Tests: run `make unittest` from the root of this branch.
R=gary.poster, bac
CC=
https:/
https:/
File server/
https:/
server/
in the juju-deployer library is responsible of
On 2013/09/23 14:23:17, bac wrote:
> s/of/for
Done.
Francesco Banconi (frankban) wrote : | # |
Thank you both for the reviews!
Preview Diff
1 | === removed file 'deps/juju-deployer-0.2.2.tar.gz' |
2 | Binary files deps/juju-deployer-0.2.2.tar.gz 2013-08-23 11:01:05 +0000 and deps/juju-deployer-0.2.2.tar.gz 1970-01-01 00:00:00 +0000 differ |
3 | === added file 'deps/juju-deployer-0.2.3.tar.gz' |
4 | Binary files deps/juju-deployer-0.2.3.tar.gz 1970-01-01 00:00:00 +0000 and deps/juju-deployer-0.2.3.tar.gz 2013-09-23 14:46:10 +0000 differ |
5 | === modified file 'hooks/utils.py' |
6 | --- hooks/utils.py 2013-09-12 19:58:07 +0000 |
7 | +++ hooks/utils.py 2013-09-23 14:46:10 +0000 |
8 | @@ -113,7 +113,7 @@ |
9 | 'futures-2.1.4.tar.gz', |
10 | 'tornado-3.1.tar.gz', |
11 | 'jujuclient-0.0.9.tar.gz', |
12 | - 'juju-deployer-0.2.2.tar.gz', |
13 | + 'juju-deployer-0.2.3.tar.gz', |
14 | ) |
15 | SERVER_DIR = os.path.join(CURRENT_DIR, 'server') |
16 | |
17 | |
18 | === modified file 'revision' |
19 | --- revision 2013-09-13 08:05:51 +0000 |
20 | +++ revision 2013-09-23 14:46:10 +0000 |
21 | @@ -1,1 +1,1 @@ |
22 | -85 |
23 | +86 |
24 | |
25 | === modified file 'server/guiserver/__init__.py' |
26 | --- server/guiserver/__init__.py 2013-09-12 17:02:58 +0000 |
27 | +++ server/guiserver/__init__.py 2013-09-23 14:46:10 +0000 |
28 | @@ -30,7 +30,7 @@ |
29 | the HTTPS connection, allowing changes in the Juju environment to be propagated |
30 | and shown immediately by the browser. """ |
31 | |
32 | -VERSION = (0, 2, 0) |
33 | +VERSION = (0, 2, 1) |
34 | |
35 | |
36 | def get_version(): |
37 | |
38 | === modified file 'server/guiserver/bundles/__init__.py' |
39 | --- server/guiserver/bundles/__init__.py 2013-09-13 08:28:12 +0000 |
40 | +++ server/guiserver/bundles/__init__.py 2013-09-23 14:46:10 +0000 |
41 | @@ -41,12 +41,12 @@ |
42 | the WebSocket request/response aspects, or how incoming data is retrieved |
43 | or generated. |
44 | |
45 | - The Deployer implementation in this module uses the juju-deployer library |
46 | - to import the provided bundle into the Juju environment. Since the |
47 | - mentioned operations are executed in a separate process, it is safe for |
48 | - the Deployer to interact with the blocking juju-deployer library. |
49 | - Those blocking functions are defined in the blocking module of this |
50 | - package, described below. |
51 | + The Deployer implementation in this package uses the juju-deployer |
52 | + library to import the provided bundle into the Juju environment. Since |
53 | + the mentioned operations are executed in a separate process, it is safe |
54 | + for the Deployer to interact with the blocking juju-deployer library. |
55 | + Those blocking functions are defined in the guiserver module of the |
56 | + juju-deployer project, described below. |
57 | |
58 | Note that the Deployer is not intended to store request related data: one |
59 | instance is created once when the application is bootstrapped and used as |
60 | @@ -60,23 +60,24 @@ |
61 | views module of this package. The DeployMiddleware dispatches requests |
62 | and collect responses to be sent back to the API client. |
63 | |
64 | -The views and blocking modules are responsible of handling the request/response |
65 | -process and of starting/scheduling bundle deployments. |
66 | +The views module is responsible for handling the request/response process and |
67 | +of starting/scheduling bundle deployments. |
68 | |
69 | - views: as already mentioned, the functions in this module handle the |
70 | requests from the API client, and set up responses. Since the views have |
71 | access to the Deployer (described above), they can start/queue bundle |
72 | deployments. |
73 | |
74 | - - blocking: all the blocking functions interacting with the juju-deployer |
75 | - library belong here. Specifically this module defines two functions: |
76 | - - validate: validate a bundle based on the state of the Juju env.; |
77 | - - import_bundle: starts the bundle deployment process. |
78 | +The deployer.guiserver module in the juju-deployer library is responsible for |
79 | +validating a bundle and starting a deployment. Specifically the module defines |
80 | +two functions: |
81 | + - validate: validate a bundle based on the state of the Juju env.; |
82 | + - import_bundle: starts the bundle deployment process. |
83 | |
84 | The infrastructure described above can be summarized like the following |
85 | (each arrow meaning "calls"): |
86 | - request handling: request -> DeployMiddleware -> views |
87 | - - deployment handling: views -> Deployer -> blocking |
88 | + - deployment handling: views -> Deployer -> deployer.guiserver |
89 | - response handling: views -> response |
90 | |
91 | While the DeployMiddleware parses the request data and statically validates |
92 | |
93 | === modified file 'server/guiserver/bundles/base.py' |
94 | --- server/guiserver/bundles/base.py 2013-09-13 08:03:38 +0000 |
95 | +++ server/guiserver/bundles/base.py 2013-09-23 14:46:10 +0000 |
96 | @@ -29,12 +29,12 @@ |
97 | process, |
98 | ProcessPoolExecutor, |
99 | ) |
100 | +from deployer import guiserver as blocking |
101 | from tornado import gen |
102 | from tornado.ioloop import IOLoop |
103 | from tornado.util import ObjectDict |
104 | |
105 | from guiserver.bundles import ( |
106 | - blocking, |
107 | utils, |
108 | views, |
109 | ) |
110 | @@ -49,6 +49,18 @@ |
111 | # Juju API versions supported by the GUI server Deployer. |
112 | # Tests use the first API version in this list. |
113 | SUPPORTED_API_VERSIONS = ['go'] |
114 | +# Options used by the juju-deployer Importer instance. |
115 | +IMPORTER_OPTIONS = ObjectDict( |
116 | + branch_only=False, # Avoid just updating VCS branches and exiting. |
117 | + deploy_delay=0, # Do not sleep between 'deploy' commands. |
118 | + no_local_mods=True, # Disallow deployment of locally-modified charms. |
119 | + overrides=None, # Do not override config options. |
120 | + rel_wait=60, # Wait for 1 minute before checking for relation errors. |
121 | + retry_count=0, # Do not retry on unit errors. |
122 | + timeout=45*60, # Set a 45 minutes timeout for the entire deployment. |
123 | + update_charms=False, # Do not update existing charm branches. |
124 | + watch=False, # Do not watch environment changes on console. |
125 | +) |
126 | |
127 | |
128 | class Deployer(object): |
129 | @@ -141,7 +153,8 @@ |
130 | # Add the import bundle job to the run executor, and set up a callback |
131 | # to be called when the import process completes. |
132 | future = self._run_executor.submit( |
133 | - blocking.import_bundle, self._apiurl, user.password, name, bundle) |
134 | + blocking.import_bundle, |
135 | + self._apiurl, user.password, name, bundle, IMPORTER_OPTIONS) |
136 | add_future(self._io_loop, future, self._import_callback, deployment_id) |
137 | self._futures[deployment_id] = future |
138 | # If a customized callback is provided, schedule it as well. |
139 | |
140 | === removed file 'server/guiserver/bundles/blocking.py' |
141 | --- server/guiserver/bundles/blocking.py 2013-08-23 12:56:19 +0000 |
142 | +++ server/guiserver/bundles/blocking.py 1970-01-01 00:00:00 +0000 |
143 | @@ -1,143 +0,0 @@ |
144 | -# This file is part of the Juju GUI, which lets users view and manage Juju |
145 | -# environments within a graphical interface (https://launchpad.net/juju-gui). |
146 | -# Copyright (C) 2013 Canonical Ltd. |
147 | -# |
148 | -# This program is free software: you can redistribute it and/or modify it under |
149 | -# the terms of the GNU Affero General Public License version 3, as published by |
150 | -# the Free Software Foundation. |
151 | -# |
152 | -# This program is distributed in the hope that it will be useful, but WITHOUT |
153 | -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
154 | -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
155 | -# Affero General Public License for more details. |
156 | -# |
157 | -# You should have received a copy of the GNU Affero General Public License |
158 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
159 | - |
160 | -"""Blocking functions and objects for handling bundle deployments. |
161 | - |
162 | -The following functions and objects use the juju-deployer library to handle |
163 | -bundle deployments. They are intended to be run in a separate process. |
164 | -Code interacting with the juju-deployer should be stored here. |
165 | -""" |
166 | - |
167 | -import os |
168 | - |
169 | -from deployer.action.importer import Importer |
170 | -from deployer.deployment import Deployment |
171 | -from deployer.env import GoEnvironment |
172 | -from tornado import util |
173 | - |
174 | -from guiserver.utils import mkdir |
175 | - |
176 | - |
177 | -IMPORTER_OPTIONS = util.ObjectDict( |
178 | - branch_only=False, # Avoid just updating VCS branches and exiting. |
179 | - deploy_delay=0, # Do not sleep between 'deploy' commands. |
180 | - no_local_mods=True, # Disallow deployment of locally-modified charms. |
181 | - overrides=None, # Do not override config options. |
182 | - rel_wait=60, # Wait for 1 minute before checking for relation errors. |
183 | - retry_count=0, # Do not retry on unit errors. |
184 | - timeout=45*60, # Set a 45 minutes timeout for the entire deployment. |
185 | - update_charms=False, # Do not update existing charm branches. |
186 | - watch=False, # Do not watch environment changes on console. |
187 | -) |
188 | -# This value is used by the juju-deployer Importer object to store charms. |
189 | -JUJU_HOME = '/var/lib/juju/gui-server/juju-home' |
190 | - |
191 | - |
192 | -class _Environment(GoEnvironment): |
193 | - """A Juju environment for the juju-deployer. |
194 | - |
195 | - Add support for deployments via the Juju API and for authenticating with |
196 | - the provided password. |
197 | - """ |
198 | - |
199 | - def __init__(self, endpoint, password): |
200 | - super(_Environment, self).__init__('go', endpoint=endpoint) |
201 | - self._password = password |
202 | - |
203 | - def _get_token(self): |
204 | - """Return the stored password. |
205 | - |
206 | - This method is overridden so that the juju-deployer does not try to |
207 | - parse the environments.yaml file in order to retrieve the admin-secret. |
208 | - """ |
209 | - return self._password |
210 | - |
211 | - def connect(self): |
212 | - """Connect the API client to the Juju backend. |
213 | - |
214 | - This method is overridden so that a call to connect is a no-op if the |
215 | - client is already connected. |
216 | - """ |
217 | - if self.client is None: |
218 | - super(_Environment, self).connect() |
219 | - |
220 | - def close(self): |
221 | - """Close the API connection. |
222 | - |
223 | - Also set the client attribute to None after the disconnection. |
224 | - """ |
225 | - super(_Environment, self).close() |
226 | - self.client = None |
227 | - |
228 | - def deploy( |
229 | - self, name, charm_url, config=None, constraints=None, num_units=1, |
230 | - *args, **kwargs): |
231 | - """Deploy a service using the API. |
232 | - |
233 | - Using the API in place of the command line introduces some limitations: |
234 | - - it is not possible to use a local charm/repository; |
235 | - - it is not possible to deploy to a specific machine. |
236 | - """ |
237 | - self.client.deploy( |
238 | - name, charm_url, config=config, constraints=constraints, |
239 | - num_units=num_units) |
240 | - |
241 | - |
242 | -def _validate(env, bundle): |
243 | - """Bundle validation logic, used by both validate and import_bundle. |
244 | - |
245 | - This function receives a connected environment and the bundle as a YAML |
246 | - decoded object. |
247 | - """ |
248 | - # Retrieve the services deployed in the Juju environment. |
249 | - env_status = env.status() |
250 | - env_services = set(env_status['services'].keys()) |
251 | - # Retrieve the services in the bundle. |
252 | - bundle_services = set(bundle.get('services', {}).keys()) |
253 | - # Calculate overlapping services. |
254 | - overlapping = env_services.intersection(bundle_services) |
255 | - if overlapping: |
256 | - services = ', '.join(overlapping) |
257 | - error = 'service(s) already in the environment: {}'.format(services) |
258 | - raise ValueError(error) |
259 | - |
260 | - |
261 | -def validate(apiurl, password, bundle): |
262 | - """Validate a bundle.""" |
263 | - env = _Environment(apiurl, password) |
264 | - env.connect() |
265 | - try: |
266 | - _validate(env, bundle) |
267 | - finally: |
268 | - env.close() |
269 | - |
270 | - |
271 | -def import_bundle(apiurl, password, name, bundle): |
272 | - """Import a bundle.""" |
273 | - env = _Environment(apiurl, password) |
274 | - deployment = Deployment(name, bundle, []) |
275 | - importer = Importer(env, deployment, IMPORTER_OPTIONS) |
276 | - env.connect() |
277 | - # The Importer tries to retrieve the Juju home from the JUJU_HOME |
278 | - # environment variable: create a customized directory (if required) and |
279 | - # set up the environment context for the Importer. |
280 | - mkdir(JUJU_HOME) |
281 | - os.environ['JUJU_HOME'] = JUJU_HOME |
282 | - try: |
283 | - _validate(env, bundle) |
284 | - importer.run() |
285 | - finally: |
286 | - env.close() |
287 | |
288 | === modified file 'server/guiserver/tests/bundles/test_base.py' |
289 | --- server/guiserver/tests/bundles/test_base.py 2013-09-13 14:09:40 +0000 |
290 | +++ server/guiserver/tests/bundles/test_base.py 2013-09-23 14:46:10 +0000 |
291 | @@ -111,7 +111,8 @@ |
292 | # Wait for the deployment to be completed. |
293 | self.wait() |
294 | mock_import_bundle.assert_called_once_with( |
295 | - self.apiurl, self.user.password, 'bundle', self.bundle) |
296 | + self.apiurl, self.user.password, 'bundle', self.bundle, |
297 | + base.IMPORTER_OPTIONS) |
298 | mock_import_bundle.assert_called_in_a_separate_process() |
299 | |
300 | def test_watch(self): |
301 | |
302 | === removed file 'server/guiserver/tests/bundles/test_blocking.py' |
303 | --- server/guiserver/tests/bundles/test_blocking.py 2013-08-23 16:06:21 +0000 |
304 | +++ server/guiserver/tests/bundles/test_blocking.py 1970-01-01 00:00:00 +0000 |
305 | @@ -1,189 +0,0 @@ |
306 | -# This file is part of the Juju GUI, which lets users view and manage Juju |
307 | -# environments within a graphical interface (https://launchpad.net/juju-gui). |
308 | -# Copyright (C) 2013 Canonical Ltd. |
309 | -# |
310 | -# This program is free software: you can redistribute it and/or modify it under |
311 | -# the terms of the GNU Affero General Public License version 3, as published by |
312 | -# the Free Software Foundation. |
313 | -# |
314 | -# This program is distributed in the hope that it will be useful, but WITHOUT |
315 | -# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
316 | -# SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
317 | -# Affero General Public License for more details. |
318 | -# |
319 | -# You should have received a copy of the GNU Affero General Public License |
320 | -# along with this program. If not, see <http://www.gnu.org/licenses/>. |
321 | - |
322 | -"""Tests for the bundles support blocking functions and objects.""" |
323 | - |
324 | -from contextlib import contextmanager |
325 | -import os |
326 | -import shutil |
327 | -import tempfile |
328 | -import unittest |
329 | - |
330 | -import mock |
331 | - |
332 | -from guiserver.bundles import blocking |
333 | -from guiserver.tests import helpers |
334 | - |
335 | - |
336 | -@mock.patch('deployer.env.go.EnvironmentClient') |
337 | -class TestEnvironment(unittest.TestCase): |
338 | - |
339 | - endpoint = 'wss://api.example.com:17070' |
340 | - password = 'Secret!' |
341 | - |
342 | - def setUp(self): |
343 | - self.env = blocking._Environment(self.endpoint, self.password) |
344 | - |
345 | - def test_connect(self, mock_client): |
346 | - # The environment uses the provided endpoint and password to connect |
347 | - # to the Juju API server. |
348 | - self.env.connect() |
349 | - mock_client.assert_called_once_with(self.endpoint) |
350 | - mock_client().login.assert_called_once_with(self.password) |
351 | - |
352 | - def test_multiple_connections(self, mock_client): |
353 | - # The environment does not attempt a second connection if it is already |
354 | - # connected to the API backend. |
355 | - self.env.connect() |
356 | - self.env.connect() |
357 | - self.assertEqual(1, mock_client.call_count) |
358 | - |
359 | - def test_close(self, mock_client): |
360 | - # The client attribute is set to None when the connection is closed. |
361 | - self.env.connect() |
362 | - self.env.close() |
363 | - self.assertIsNone(self.env.client) |
364 | - |
365 | - def test_deploy(self, mock_client): |
366 | - # The environment uses the API to deploy charms. |
367 | - self.env.connect() |
368 | - config = {'foo': 'bar'} |
369 | - constraints = {'cpu': 4} |
370 | - # Deploy a service: the last two arguments (force_machine and repo) are |
371 | - # ignored. |
372 | - self.env.deploy( |
373 | - 'myservice', 'cs:precise/service-42', config=config, |
374 | - constraints=constraints, num_units=2, force_machine=1, repo='/tmp') |
375 | - mock_client().deploy.assert_called_once_with( |
376 | - 'myservice', 'cs:precise/service-42', config=config, |
377 | - constraints=constraints, num_units=2) |
378 | - |
379 | - |
380 | -class DeployerFunctionsTestMixin(helpers.BundlesTestMixin): |
381 | - """Base set up for the functions that make use of the juju-deployer.""" |
382 | - |
383 | - apiurl = 'wss://api.example.com:17070' |
384 | - password = 'Secret!' |
385 | - |
386 | - def setUp(self): |
387 | - self.name, self.bundle = self.get_name_and_bundle() |
388 | - |
389 | - def check_environment_life(self, mock_environment): |
390 | - """Check the calls executed on the given mock environment. |
391 | - |
392 | - Ensure that, in order to retrieve the list of currently deployed |
393 | - services, the environment is instantiated, connected, env.status is |
394 | - called and then the connection is closed. |
395 | - """ |
396 | - mock_environment.assert_called_once_with(self.apiurl, self.password) |
397 | - mock_env_instance = mock_environment() |
398 | - mock_env_instance.connect.assert_called_once_with() |
399 | - mock_env_instance.status.assert_called_once_with() |
400 | - mock_env_instance.close.assert_called_once_with() |
401 | - |
402 | - @contextmanager |
403 | - def assert_overlapping_services(self, mock_environment): |
404 | - """Ensure a ValueError is raised in the context manager block. |
405 | - |
406 | - The given mock environment object is set up so that its status |
407 | - simulates an existing service. The name of this service overlaps with |
408 | - the name of one of the services in the bundle. |
409 | - """ |
410 | - mock_env_instance = mock_environment() |
411 | - mock_env_instance.status.return_value = {'services': {'mysql': {}}} |
412 | - # Ensure a ValueError is raised by the code in the context block. |
413 | - with self.assertRaises(ValueError) as context_manager: |
414 | - yield |
415 | - # The error reflects the overlapping service name. |
416 | - error = str(context_manager.exception) |
417 | - self.assertEqual('service(s) already in the environment: mysql', error) |
418 | - # Even if an error occurs, the environment connection is closed. |
419 | - mock_env_instance.close.assert_called_once_with() |
420 | - |
421 | - |
422 | -@mock.patch('guiserver.bundles.blocking._Environment') |
423 | -class TestValidate(DeployerFunctionsTestMixin, unittest.TestCase): |
424 | - |
425 | - def test_validation(self, mock_environment): |
426 | - # The validation is correctly run. |
427 | - blocking.validate(self.apiurl, self.password, self.bundle) |
428 | - # The environment is correctly instantiated and used. |
429 | - self.check_environment_life(mock_environment) |
430 | - |
431 | - def test_overlapping_services(self, mock_environment): |
432 | - # The validation fails if the bundle includes a service name already |
433 | - # present in the Juju environment. |
434 | - with self.assert_overlapping_services(mock_environment): |
435 | - blocking.validate(self.apiurl, self.password, self.bundle) |
436 | - |
437 | - |
438 | -@mock.patch('guiserver.bundles.blocking._Environment') |
439 | -class TestImportBundle(DeployerFunctionsTestMixin, unittest.TestCase): |
440 | - |
441 | - @contextmanager |
442 | - def patch_juju_home(self): |
443 | - """Patch the value used by the bundle importer as Juju home.""" |
444 | - base_dir = tempfile.mkdtemp() |
445 | - self.addCleanup(shutil.rmtree, base_dir) |
446 | - juju_home = os.path.join(base_dir, 'juju-home') |
447 | - with mock.patch('guiserver.bundles.blocking.JUJU_HOME', juju_home): |
448 | - yield juju_home |
449 | - |
450 | - @mock.patch('guiserver.bundles.blocking.Importer') |
451 | - def test_importing_bundle(self, mock_importer, mock_environment): |
452 | - # The juju-deployer importer is correctly set up and run. |
453 | - with self.patch_juju_home(): |
454 | - blocking.import_bundle( |
455 | - self.apiurl, self.password, self.name, self.bundle) |
456 | - # The environment is correctly instantiated and used. |
457 | - self.check_environment_life(mock_environment) |
458 | - # The importer is correctly instantiated. |
459 | - self.assertEqual(1, mock_importer.call_count) |
460 | - importer_args = mock_importer.call_args[0] |
461 | - self.assertEqual(3, len(importer_args)) |
462 | - env, deployment, options = importer_args |
463 | - # The first argument passed to the importer is the environment. |
464 | - self.assertIs(mock_environment(), env) |
465 | - # The second argument is the deployment object. |
466 | - self.assertIsInstance(deployment, blocking.Deployment) |
467 | - self.assertEqual(self.name, deployment.name) |
468 | - self.assertEqual(self.bundle, deployment.data) |
469 | - # The third and last argument is the options object. |
470 | - self.assertIs(blocking.IMPORTER_OPTIONS, options) |
471 | - # The importer is started. |
472 | - mock_importer().run.assert_called_once_with() |
473 | - |
474 | - def test_overlapping_services(self, mock_environment): |
475 | - # The import fails if the bundle includes a service name already |
476 | - # present in the Juju environment. |
477 | - with self.assert_overlapping_services(mock_environment): |
478 | - with self.patch_juju_home(): |
479 | - blocking.import_bundle( |
480 | - self.apiurl, self.password, self.name, self.bundle) |
481 | - |
482 | - @mock.patch('guiserver.bundles.blocking.Importer') |
483 | - def test_juju_home(self, mock_importer, mock_environment): |
484 | - # A customized Juju home is created and used during the import process. |
485 | - with self.patch_juju_home() as juju_home: |
486 | - assert not os.path.isdir(juju_home), 'directory should not exist' |
487 | - # Ensure JUJU_HOME is included in the context when the Importer |
488 | - # instance is run. |
489 | - run = lambda: self.assertEqual(juju_home, os.getenv('JUJU_HOME')) |
490 | - mock_importer().run = run |
491 | - blocking.import_bundle( |
492 | - self.apiurl, self.password, self.name, self.bundle) |
493 | - # The JUJU_HOME directory has been created. |
494 | - self.assertTrue(os.path.isdir(juju_home)) |
495 | |
496 | === modified file 'server/guiserver/tests/helpers.py' |
497 | --- server/guiserver/tests/helpers.py 2013-08-26 08:07:36 +0000 |
498 | +++ server/guiserver/tests/helpers.py 2013-09-23 14:46:10 +0000 |
499 | @@ -25,7 +25,6 @@ |
500 | |
501 | import mock |
502 | from tornado import websocket |
503 | -import yaml |
504 | |
505 | from guiserver import auth |
506 | from guiserver.bundles import base |
507 | @@ -140,52 +139,6 @@ |
508 | """Add helper methods for testing the GUI server bundles support.""" |
509 | |
510 | apiurl = 'wss://api.example.com:17070' |
511 | - bundle = """ |
512 | - envExport: |
513 | - series: precise |
514 | - services: |
515 | - wordpress: |
516 | - charm: "cs:precise/wordpress-15" |
517 | - num_units: 1 |
518 | - options: |
519 | - debug: "no" |
520 | - engine: nginx |
521 | - tuning: single |
522 | - "wp-content": "" |
523 | - annotations: |
524 | - "gui-x": 313 |
525 | - "gui-y": 51 |
526 | - mysql: |
527 | - charm: "cs:precise/mysql-26" |
528 | - num_units: 1 |
529 | - options: |
530 | - "binlog-format": MIXED |
531 | - "block-size": "5" |
532 | - "dataset-size": "80%" |
533 | - flavor: distro |
534 | - "ha-bindiface": eth0 |
535 | - "ha-mcastport": "5411" |
536 | - "max-connections": "-1" |
537 | - "preferred-storage-engine": InnoDB |
538 | - "query-cache-size": "-1" |
539 | - "query-cache-type": "OFF" |
540 | - "rbd-name": mysql1 |
541 | - "tuning-level": safest |
542 | - vip: "" |
543 | - vip_cidr: "24" |
544 | - vip_iface: eth0 |
545 | - annotations: |
546 | - "gui-x": 669.5 |
547 | - "gui-y": -33.5 |
548 | - relations: |
549 | - - - "wordpress:db" |
550 | - - "mysql:db" |
551 | - """ |
552 | - |
553 | - def get_name_and_bundle(self): |
554 | - """Return a tuple (bundle name, contents) parsing self.bundle.""" |
555 | - all_contents = yaml.load(self.bundle) |
556 | - return all_contents.items()[0] |
557 | |
558 | def make_deployer(self, apiversion=base.SUPPORTED_API_VERSIONS[0]): |
559 | """Create and return a Deployer instance.""" |
560 | @@ -243,12 +196,13 @@ |
561 | def patch_validate(self, side_effect=None): |
562 | """Mock the blocking validate function.""" |
563 | mock_validate = MultiProcessMock(side_effect=side_effect) |
564 | - return mock.patch('guiserver.bundles.blocking.validate', mock_validate) |
565 | + validate_path = 'guiserver.bundles.base.blocking.validate' |
566 | + return mock.patch(validate_path, mock_validate) |
567 | |
568 | def patch_import_bundle(self, side_effect=None): |
569 | """Mock the blocking import_bundle function.""" |
570 | mock_import_bundle = MultiProcessMock(side_effect=side_effect) |
571 | - import_bundle_path = 'guiserver.bundles.blocking.import_bundle' |
572 | + import_bundle_path = 'guiserver.bundles.base.blocking.import_bundle' |
573 | return mock.patch(import_bundle_path, mock_import_bundle) |
574 | |
575 | |
576 | |
577 | === modified file 'server/guiserver/tests/test_utils.py' |
578 | --- server/guiserver/tests/test_utils.py 2013-08-23 15:22:19 +0000 |
579 | +++ server/guiserver/tests/test_utils.py 2013-09-23 14:46:10 +0000 |
580 | @@ -17,9 +17,6 @@ |
581 | """Tests for the Juju GUI server utilities.""" |
582 | |
583 | import json |
584 | -import os |
585 | -import shutil |
586 | -import tempfile |
587 | import unittest |
588 | |
589 | import mock |
590 | @@ -113,54 +110,6 @@ |
591 | self.assertIsNone(utils.json_decode_dict('"not-a-dict"')) |
592 | |
593 | |
594 | -class TestMkdir(unittest.TestCase): |
595 | - |
596 | - def setUp(self): |
597 | - self.playground = tempfile.mkdtemp() |
598 | - self.addCleanup(shutil.rmtree, self.playground) |
599 | - |
600 | - def test_create_dir(self): |
601 | - # A directory is correctly created. |
602 | - path = os.path.join(self.playground, 'foo') |
603 | - utils.mkdir(path) |
604 | - self.assertTrue(os.path.isdir(path)) |
605 | - |
606 | - def test_intermediate_dirs(self): |
607 | - # All intermediate directories are created. |
608 | - path = os.path.join(self.playground, 'foo', 'bar', 'leaf') |
609 | - utils.mkdir(path) |
610 | - self.assertTrue(os.path.isdir(path)) |
611 | - |
612 | - def test_expand_user(self): |
613 | - # The ~ construction is expanded. |
614 | - with mock.patch('os.environ', {'HOME': self.playground}): |
615 | - utils.mkdir('~/in/my/home') |
616 | - path = os.path.join(self.playground, 'in', 'my', 'home') |
617 | - self.assertTrue(os.path.isdir(path)) |
618 | - |
619 | - def test_existing_dir(self): |
620 | - # The function exits without errors if the target directory exists. |
621 | - path = os.path.join(self.playground, 'foo') |
622 | - os.mkdir(path) |
623 | - utils.mkdir(path) |
624 | - |
625 | - def test_existing_file(self): |
626 | - # An OSError is raised if a file already exists in the target path. |
627 | - path = os.path.join(self.playground, 'foo') |
628 | - with open(path, 'w'): |
629 | - with self.assertRaises(OSError): |
630 | - utils.mkdir(path) |
631 | - |
632 | - def test_failure(self): |
633 | - # Errors are correctly re-raised. |
634 | - path = os.path.join(self.playground, 'foo') |
635 | - os.chmod(self.playground, 0000) |
636 | - self.addCleanup(os.chmod, self.playground, 0700) |
637 | - with self.assertRaises(OSError): |
638 | - utils.mkdir(os.path.join(path)) |
639 | - self.assertFalse(os.path.exists(path)) |
640 | - |
641 | - |
642 | class TestRequestSummary(unittest.TestCase): |
643 | |
644 | def test_summary(self): |
645 | |
646 | === modified file 'server/guiserver/utils.py' |
647 | --- server/guiserver/utils.py 2013-08-23 15:22:19 +0000 |
648 | +++ server/guiserver/utils.py 2013-09-23 14:46:10 +0000 |
649 | @@ -17,10 +17,8 @@ |
650 | """Juju GUI server utility functions and classes.""" |
651 | |
652 | import collections |
653 | -import errno |
654 | import functools |
655 | import logging |
656 | -import os |
657 | import urlparse |
658 | import weakref |
659 | |
660 | @@ -68,21 +66,6 @@ |
661 | return data |
662 | |
663 | |
664 | -def mkdir(path): |
665 | - """Create a leaf directory and all intermediate ones. |
666 | - |
667 | - Also expand ~ and ~user constructions. |
668 | - If path exists and it's a directory, return without errors. |
669 | - """ |
670 | - path = os.path.expanduser(path) |
671 | - try: |
672 | - os.makedirs(path) |
673 | - except OSError as err: |
674 | - # Re-raise the error if the target path exists but it is not a dir. |
675 | - if (err.errno != errno.EEXIST) or (not os.path.isdir(path)): |
676 | - raise |
677 | - |
678 | - |
679 | def request_summary(request): |
680 | """Return a string representing a summary for the given request.""" |
681 | return '{} {} ({})'.format(request.method, request.uri, request.remote_ip) |
Reviewers: mp+187015_ code.launchpad. net,
Message:
Please take a look.
Description:
New juju-deployer darwin version.
Switched to the new juju-deployer version,
which includes support for deployments started
by the GUI server.
This allows us to remove the blocking/ deployer- specific
code from the GUI server.
Also updated the relevant parts of the documentation.
Tests: run `make unittest` from the root of this branch.
https:/ /code.launchpad .net/~frankban/ charms/ precise/ juju-gui/ new-darwin/ +merge/ 187015
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/13824045/
Affected files (+37, -466 lines): deployer- 0.2.2.tar. gz deployer- 0.2.3.tar. gz guiserver/ __init_ _.py guiserver/ bundles/ __init_ _.py guiserver/ bundles/ base.py guiserver/ bundles/ blocking. py guiserver/ tests/bundles/ test_base. py guiserver/ tests/bundles/ test_blocking. py guiserver/ tests/helpers. py guiserver/ tests/test_ utils.py guiserver/ utils.py
A [revision details]
D deps/juju-
A deps/juju-
M hooks/utils.py
M revision
M server/
M server/
M server/
D server/
M server/
D server/
M server/
M server/
M server/