Merge lp:~johnsca/charms/trusty/cloudfoundry/cfdeploy-errors into lp:~cf-charmers/charms/trusty/cloudfoundry/trunk

Proposed by Cory Johns
Status: Merged
Merged at revision: 163
Proposed branch: lp:~johnsca/charms/trusty/cloudfoundry/cfdeploy-errors
Merge into: lp:~cf-charmers/charms/trusty/cloudfoundry/trunk
Diff against target: 198 lines (+70/-36)
3 files modified
cfdeploy (+13/-7)
cloudfoundry/utils.py (+50/-26)
reconciler/ui/app.py (+7/-3)
To merge this branch: bzr merge lp:~johnsca/charms/trusty/cloudfoundry/cfdeploy-errors
Reviewer Review Type Date Requested Status
Benjamin Saller (community) Approve
Review via email: mp+243607@code.launchpad.net

Description of the change

Fixed cfdeploy handling of errors during bootstrap and deploy
Improved -l option to log to a file and assume DEBUG

To post a comment you must log in.
Revision history for this message
Benjamin Saller (bcsaller) wrote :

LGTM +1

review: Approve
164. By Cory Johns

Improvements to reconciler websocket handling to hopefully prevent errors in log and long delays on initial load

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'cfdeploy'
--- cfdeploy 2014-11-13 16:22:27 +0000
+++ cfdeploy 2014-12-04 19:30:36 +0000
@@ -61,6 +61,8 @@
61 webadmin_endpoint,61 webadmin_endpoint,
62 sh,62 sh,
63 socket_open,63 socket_open,
64 retry,
65 wait_for,
64 until,66 until,
65 which67 which
66 )68 )
@@ -72,7 +74,8 @@
72 parser = argparse.ArgumentParser()74 parser = argparse.ArgumentParser()
73 parser.add_argument('-e', '--env', default=current_env())75 parser.add_argument('-e', '--env', default=current_env())
74 parser.add_argument('-v', '--version', default="latest")76 parser.add_argument('-v', '--version', default="latest")
75 parser.add_argument('-l', '--log-level', default=logging.INFO)77 parser.add_argument('-l', '--log', action='store_true',
78 help='Write debug log to cfdeploy.log')
76 parser.add_argument('-c', '--constraints')79 parser.add_argument('-c', '--constraints')
77 parser.add_argument('-g', '--generate', action='store_true')80 parser.add_argument('-g', '--generate', action='store_true')
78 parser.add_argument('admin_password')81 parser.add_argument('admin_password')
@@ -136,18 +139,21 @@
136139
137def main():140def main():
138 options = setup()141 options = setup()
139 logging.basicConfig(level=options.log_level)142 if options.log:
143 root_logger = logging.getLogger()
144 root_logger.handlers = [logging.FileHandler('cfdeploy.log', 'w')]
145 root_logger.setLevel(logging.DEBUG)
140 install_deps()146 install_deps()
141147
142 bar = ProgressBar('Deploying CloudFoundry', max=10)148 bar = ProgressBar('Deploying CloudFoundry', max=10)
143 bar.start()149 bar.start()
144 bar.next(message='Bootstrapping')150 retry(3, bootstrap, bar=bar, message='Bootstrapping')
145 bootstrap()
146 until(juju_state_server, bar=bar, message="Waiting for State Server")151 until(juju_state_server, bar=bar, message="Waiting for State Server")
147 bar.next(message='Deploying Orchestrator')152 bar.next(message='Deploying Orchestrator')
148 deploy(constraints=options.constraints,153 wait_for(30, 5, partial(deploy,
149 generate_dependents=options.generate,154 constraints=options.constraints,
150 admin_password=options.admin_password)155 generate_dependents=options.generate,
156 admin_password=options.admin_password))
151 until(lambda: socket_open(reconciler_endpoint(), 8888),157 until(lambda: socket_open(reconciler_endpoint(), 8888),
152 bar=bar, message="Waiting on Reconciler")158 bar=bar, message="Waiting on Reconciler")
153 bar.next(message='Showing Reconciler')159 bar.next(message='Showing Reconciler')
154160
=== modified file 'cloudfoundry/utils.py'
--- cloudfoundry/utils.py 2014-11-12 18:24:39 +0000
+++ cloudfoundry/utils.py 2014-12-04 19:30:36 +0000
@@ -184,7 +184,7 @@
184 **kwargs)184 **kwargs)
185 output, _ = p.communicate()185 output, _ = p.communicate()
186 ret_code = p.poll()186 ret_code = p.poll()
187 logging.debug('result: %s', output)187 logging.debug('output: %s', output)
188 if check is True:188 if check is True:
189 if ret_code != 0 and throw:189 if ret_code != 0 and throw:
190 raise subprocess.CalledProcessError(ret_code, all_args, output=output)190 raise subprocess.CalledProcessError(ret_code, all_args, output=output)
@@ -265,6 +265,23 @@
265 return wait_for(0, 20, *callbacks, **kwargs)265 return wait_for(0, 20, *callbacks, **kwargs)
266266
267267
268def retry(attempts, *callbacks, **kwargs):
269 """
270 Repeatedly try callbacks a fixed number of times or until all return True
271 """
272 for attempt in xrange(attempts):
273 if 'bar' in kwargs:
274 kwargs['bar'].next(attempt == 0, message=kwargs.get('message'))
275 for callback in callbacks:
276 if not callback():
277 break
278 else:
279 break
280 else:
281 raise OSError("Retry attempts exceeded")
282 return True
283
284
268def juju_state_server():285def juju_state_server():
269 if api_endpoints() != 0:286 if api_endpoints() != 0:
270 return False287 return False
@@ -356,7 +373,9 @@
356373
357def bootstrap():374def bootstrap():
358 if not os.path.exists(get_jenv()) or api_endpoints() != 0:375 if not os.path.exists(get_jenv()) or api_endpoints() != 0:
359 sh.juju('bootstrap')376 juju = sh.check('juju', throw=False)
377 return juju('bootstrap') != 0
378 return True
360379
361380
362def get_admin_password():381def get_admin_password():
@@ -386,30 +405,35 @@
386def deploy(**config):405def deploy(**config):
387 status = get_client().status()406 status = get_client().status()
388 constraints = config.pop('constraints', None)407 constraints = config.pop('constraints', None)
389 if 'cloudfoundry' not in status['Services']:408 if 'cloudfoundry' in status['Services']:
390 # create an up to date config409 return True
391 config = dict({410 # create an up to date config
392 'admin_secret': get_admin_password(),411 config = dict({
393 'cf_version': 'latest',412 'admin_secret': get_admin_password(),
394 'placement': 'dense',413 'cf_version': 'latest',
395 }, **config)414 'placement': 'dense',
396415 }, **config)
397 fd, fn = tempfile.mkstemp(suffix='.yaml')416
398 os.close(fd)417 fd, fn = tempfile.mkstemp(suffix='.yaml')
399 with open(fn, 'w') as fp:418 os.close(fd)
400 yaml.dump({'cloudfoundry': config}, fp)419 with open(fn, 'w') as fp:
401420 yaml.dump({'cloudfoundry': config}, fp)
402 repo_path = get_repo_path()421
403422 repo_path = get_repo_path()
404 args = ['deploy', '--config=%s' % fn,423
405 '--repository=%s' % repo_path]424 args = ['deploy', '--config=%s' % fn,
406 if constraints:425 '--repository=%s' % repo_path]
407 args.append('--constraints=%s' % constraints)426 if constraints:
408 args.append('local:trusty/cloudfoundry')427 args.append('--constraints=%s' % constraints)
409 sh.juju(*args)428 args.append('local:trusty/cloudfoundry')
410 time.sleep(5)429 juju = sh.check('juju', throw=False)
411 sh.juju('expose', 'cloudfoundry')430 if juju(*args) != 0:
412 os.unlink(fn)431 return False
432 time.sleep(5)
433 if juju('expose', 'cloudfoundry') != 0:
434 return False
435 os.unlink(fn)
436 return True
413437
414438
415def login(password):439def login(password):
416440
=== modified file 'reconciler/ui/app.py'
--- reconciler/ui/app.py 2014-10-17 22:34:42 +0000
+++ reconciler/ui/app.py 2014-12-04 19:30:36 +0000
@@ -1,4 +1,5 @@
1import json1import json
2import logging
2from path import path3from path import path
3from tornado.websocket import WebSocketHandler4from tornado.websocket import WebSocketHandler
4import tornado.ioloop5import tornado.ioloop
@@ -29,7 +30,7 @@
29 listeners = {}30 listeners = {}
3031
31 def open(self):32 def open(self):
32 self.connections.add(self)33 DashboardIO.connections.add(self)
3334
34 def on_message(self, message):35 def on_message(self, message):
35 """36 """
@@ -43,7 +44,7 @@
43 listener(data)44 listener(data)
4445
45 def on_close(self):46 def on_close(self):
46 self.connections.remove(self)47 DashboardIO.connections.discard(self)
4748
48 @classmethod49 @classmethod
49 def send(cls, message):50 def send(cls, message):
@@ -51,7 +52,10 @@
51 Send a message out over all connected WebSockets.52 Send a message out over all connected WebSockets.
52 """53 """
53 for connection in cls.connections:54 for connection in cls.connections:
54 connection.write_message(message)55 try:
56 connection.write_message(message)
57 except:
58 logging.error('Error sending message', exc_info=True)
5559
56 @classmethod60 @classmethod
57 def add_listener(cls, message_type, listener):61 def add_listener(cls, message_type, listener):

Subscribers

People subscribed via source and target branches