Merge lp:~frankban/juju-deployer/improve-guiserver-feedback into lp:juju-deployer

Proposed by Francesco Banconi
Status: Merged
Merged at revision: 94
Proposed branch: lp:~frankban/juju-deployer/improve-guiserver-feedback
Merge into: lp:juju-deployer
Diff against target: 151 lines (+102/-4)
2 files modified
deployer/guiserver.py (+31/-1)
deployer/tests/test_guiserver.py (+71/-3)
To merge this branch: bzr merge lp:~frankban/juju-deployer/improve-guiserver-feedback
Reviewer Review Type Date Requested Status
Kapil Thangavelu Approve
Review via email: mp+203915@code.launchpad.net

Commit message

Improve GUI server feedback handling

Description of the change

Improve how feedback errors are propagated from the deployer to the GUI server.

To post a comment you must log in.
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

looks good, merged.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'deployer/guiserver.py'
--- deployer/guiserver.py 2013-08-29 16:35:45 +0000
+++ deployer/guiserver.py 2014-01-30 10:51:34 +0000
@@ -23,6 +23,36 @@
23JUJU_HOME = '/var/lib/juju-gui/juju-home'23JUJU_HOME = '/var/lib/juju-gui/juju-home'
2424
2525
26class DeploymentError(Exception):
27 """One or more errors occurred during the deployment preparation."""
28
29 def __init__(self, errors):
30 self.errors = errors
31 super(DeploymentError, self).__init__(errors)
32
33 def __str__(self):
34 return '\n'.join(self.errors)
35
36
37class GUIDeployment(Deployment):
38 """Handle bundle deployments requested by the GUI server."""
39
40 def __init__(self, name, data):
41 super(GUIDeployment, self).__init__(name, data, [])
42
43 def _handle_feedback(self, feedback):
44 """Raise a DeploymentError if the given feedback includes errors.
45
46 The GUI server will catch and report failures propagating them through
47 the WebSocket connection to the client.
48 """
49 for message in feedback.get_warnings():
50 self.log.warning(message)
51 if feedback.has_errors:
52 # Errors are logged by the GUI server.
53 raise DeploymentError(feedback.get_errors())
54
55
26def _validate(env, bundle):56def _validate(env, bundle):
27 """Bundle validation logic, used by both validate and import_bundle.57 """Bundle validation logic, used by both validate and import_bundle.
2858
@@ -55,7 +85,7 @@
55def import_bundle(apiurl, password, name, bundle, options):85def import_bundle(apiurl, password, name, bundle, options):
56 """Import a bundle."""86 """Import a bundle."""
57 env = GUIEnvironment(apiurl, password)87 env = GUIEnvironment(apiurl, password)
58 deployment = Deployment(name, bundle, [])88 deployment = GUIDeployment(name, bundle)
59 importer = Importer(env, deployment, options)89 importer = Importer(env, deployment, options)
60 env.connect()90 env.connect()
61 # The Importer tries to retrieve the Juju home from the JUJU_HOME91 # The Importer tries to retrieve the Juju home from the JUJU_HOME
6292
=== modified file 'deployer/tests/test_guiserver.py'
--- deployer/tests/test_guiserver.py 2014-01-23 14:14:28 +0000
+++ deployer/tests/test_guiserver.py 2014-01-30 10:51:34 +0000
@@ -14,11 +14,53 @@
14 cli,14 cli,
15 guiserver,15 guiserver,
16)16)
17from deployer.deployment import Deployment17from deployer.feedback import Feedback
18
19from deployer.tests.base import TEST_OFFLINE18from deployer.tests.base import TEST_OFFLINE
2019
2120
21class TestDeploymentError(unittest.TestCase):
22
23 def test_error(self):
24 # A single error is properly stored and represented.
25 exception = guiserver.DeploymentError(['bad wolf'])
26 self.assertEqual(['bad wolf'], exception.errors)
27 self.assertEqual('bad wolf', str(exception))
28
29 def test_multiple_errors(self):
30 # Multiple deployment errors are properly stored and represented.
31 errors = ['error 1', 'error 2']
32 exception = guiserver.DeploymentError(errors)
33 self.assertEqual(errors, exception.errors)
34 self.assertEqual('error 1\nerror 2', str(exception))
35
36
37class TestGUIDeployment(unittest.TestCase):
38
39 def setUp(self):
40 # Set up a GUIDeployment instance and a Feedback object.
41 self.deployment = guiserver.GUIDeployment('mybundle', 'mydata')
42 self.feedback = Feedback()
43
44 def test_valid_deployment(self):
45 # If the bundle is well formed, the deployment proceeds normally.
46 self.assertIsNone(self.deployment._handle_feedback(self.feedback))
47
48 def test_warnings(self):
49 # Warning messages are properly logged.
50 self.feedback.warn('we are the Borg')
51 with mock.patch.object(self.deployment, 'log') as mock_log:
52 self.deployment._handle_feedback(self.feedback)
53 mock_log.warning.assert_called_once_with('we are the Borg')
54
55 def test_errors(self):
56 # A DeploymentError is raised if errors are found in the bundle.
57 self.feedback.error('error 1')
58 self.feedback.error('error 2')
59 with self.assertRaises(guiserver.DeploymentError) as cm:
60 self.deployment._handle_feedback(self.feedback)
61 self.assertEqual(['error 1', 'error 2'], cm.exception.errors)
62
63
22class DeployerFunctionsTestMixin(object):64class DeployerFunctionsTestMixin(object):
23 """Base set up for the functions that make use of the juju-deployer."""65 """Base set up for the functions that make use of the juju-deployer."""
2466
@@ -149,7 +191,7 @@
149 # The first argument passed to the importer is the environment.191 # The first argument passed to the importer is the environment.
150 self.assertIs(mock_environment(), env)192 self.assertIs(mock_environment(), env)
151 # The second argument is the deployment object.193 # The second argument is the deployment object.
152 self.assertIsInstance(deployment, Deployment)194 self.assertIsInstance(deployment, guiserver.GUIDeployment)
153 self.assertEqual(self.name, deployment.name)195 self.assertEqual(self.name, deployment.name)
154 self.assertEqual(self.bundle, deployment.data)196 self.assertEqual(self.bundle, deployment.data)
155 # The third and last argument is the options object.197 # The third and last argument is the options object.
@@ -207,6 +249,32 @@
207 mock.call.close(),249 mock.call.close(),
208 ], any_order=True)250 ], any_order=True)
209251
252 def test_deployment_errors(self, mock_environment):
253 # A DeploymentError is raised if the deployment fails.
254 bundle = {
255 'services': {
256 'wordpress': {
257 'charm': 'cs:precise/wordpress-20',
258 'options': {'no-such-option': 42}, # Invalid options.
259 },
260 'mysql': {
261 'charm': 'cs:precise/mysql-28',
262 'options': {'bad': 'wolf'}, # Invalid options.
263 },
264 },
265 }
266 self.addCleanup(self.cleanup_series_path)
267 with self.patch_juju_home():
268 with self.assertRaises(guiserver.DeploymentError) as cm:
269 guiserver.import_bundle(
270 self.apiurl, self.password, self.name, bundle,
271 self.options)
272 expected_errors = set([
273 'Invalid config charm cs:precise/wordpress-20 no-such-option=42',
274 'Invalid config charm cs:precise/mysql-28 bad=wolf',
275 ])
276 self.assertEqual(expected_errors, set(cm.exception.errors))
277
210 def test_options(self, mock_environment):278 def test_options(self, mock_environment):
211 # Ensure the default options are sane for us.279 # Ensure the default options are sane for us.
212 expected = argparse.Namespace(280 expected = argparse.Namespace(

Subscribers

People subscribed via source and target branches