diff options
-rwxr-xr-x | check | 2 | ||||
-rwxr-xr-x | lorry-controller-webapp | 51 | ||||
-rw-r--r-- | yarns.webapp/020-status.yarn | 3 | ||||
-rw-r--r-- | yarns.webapp/030-queue-management.yarn | 8 | ||||
-rw-r--r-- | yarns.webapp/900-implementations.yarn | 46 |
5 files changed, 89 insertions, 21 deletions
@@ -8,6 +8,6 @@ set -eu # a system with the necessary dependencies installed properly. (BR-13 also # doesn't have a working sqlite3 Python binding, and that's not so easy to # fix without a fixed system deployed.) -export PYTHONPATH="/home/richardmaw/workspace/baserock/src/bottle:/home/richardmaw/workspace/baserock/src/flup" +export PYTHONPATH="/home/root/new-lorry-controller/bottle:/home/root/new-lorry-controller/flup" yarn -s yarns.webapp/yarn.sh yarns.webapp/*.yarn --env PYTHONPATH="$PYTHONPATH" "$@" diff --git a/lorry-controller-webapp b/lorry-controller-webapp index 9064d17..27c6375 100755 --- a/lorry-controller-webapp +++ b/lorry-controller-webapp @@ -27,6 +27,27 @@ import cliapp from flup.server.fcgi import WSGIServer +STATUS_HTML_TEMPLATE = ''' +<!DOCTYPE HTML> +<html> + <head> + <title>Lorry Controller status</title> + </head> + <body> + <blockquote>{quote}</blockquote> + + <h1>Status of Lorry Controller</h1> + + <p>Running queue: {running-queue}.</p> + + <hr> + + <p>Updated: {timestamp}</p> + </body> +</html> +''' + + class StateDB(object): '''A wrapper around raw Sqlite for STATEDB.''' @@ -51,12 +72,16 @@ class StateDB(object): self._open() c = self._conn.cursor() for (running,) in c.execute('SELECT running FROM running_queue'): - return running + return bool(running) def set_running_queue(self, new_status): + if new_status: + new_value = 1 + else: + new_value = 0 self._open() c = self._conn.cursor() - c.execute('UPDATE running_queue SET running = ?', str(new_status)) + c.execute('UPDATE running_queue SET running = ?', str(new_value)) self._conn.commit() @@ -76,7 +101,8 @@ class LorryControllerRoute(object): ''' - def __init__(self, statedb): + def __init__(self, app_settings, statedb): + self.app_settings = app_settings self.statedb = statedb def run(self, **kwargs): @@ -95,10 +121,17 @@ class Status(LorryControllerRoute): "I'm giving her all she's got, Captain!", ] import random - return { - 'quote': '%s\n' % random.choice(quotes), + status = { + 'quote': '%s' % random.choice(quotes), 'running-queue': self.statedb.get_running_queue(), + 'timestamp': time.strftime('%Y-%m-%d %H:%M:%S'), } + self.write_static_status_page(status) + return status + + def write_static_status_page(self, status): + with open(self.app_settings['status-html'], 'w') as f: + f.write(STATUS_HTML_TEMPLATE.format(**status)) class StartQueue(LorryControllerRoute): @@ -129,6 +162,12 @@ class WEBAPP(cliapp.Application): 'use FILE as the state database', metavar='FILE') + self.settings.string( + ['status-html'], + 'write a static HTML page to FILE to describe overall status', + metavar='FILE', + default='/dev/null') + self.settings.boolean( ['wsgi'], 'run in wsgi mode (default is debug mode, for development)') @@ -174,7 +213,7 @@ class WEBAPP(cliapp.Application): webapp = bottle.Bottle() for route_class in self.find_routes(): - route = route_class(statedb) + route = route_class(self.settings, statedb) webapp.route( path=route.path, method=route.http_method, diff --git a/yarns.webapp/020-status.yarn b/yarns.webapp/020-status.yarn index ba8c903..4f0ce02 100644 --- a/yarns.webapp/020-status.yarn +++ b/yarns.webapp/020-status.yarn @@ -9,6 +9,7 @@ it has no Lorry or Trove specs. GIVEN a running WEBAPP WHEN admin makes request GET /1.0/status THEN response is JSON - AND response has running-queue set to False + AND response has running-queue set to false + AND static status page got updated FINALLY WEBAPP terminates diff --git a/yarns.webapp/030-queue-management.yarn b/yarns.webapp/030-queue-management.yarn index 75b17e3..b58a63f 100644 --- a/yarns.webapp/030-queue-management.yarn +++ b/yarns.webapp/030-queue-management.yarn @@ -13,22 +13,22 @@ new jobs, and later to start it again. SCENARIO admin can start and stop WEBAPP job scheduling GIVEN a running WEBAPP WHEN admin makes request GET /1.0/status - THEN response has running-queue set to False + THEN response has running-queue set to false WHEN admin makes request GET /1.0/start-queue AND admin makes request GET /1.0/status - THEN response has running-queue set to True + THEN response has running-queue set to true Further, the state change needs to be persistent across WEBAPP instances, so we kill the WEBAPP that's currently running, and start a -new one, and verify that the `running-queue` status is still `True`. +new one, and verify that the `running-queue` status is still `true`. WHEN WEBAPP is terminated THEN WEBAPP isn't running GIVEN a running WEBAPP WHEN admin makes request GET /1.0/status - THEN response has running-queue set to True + THEN response has running-queue set to true Finally, clean up. diff --git a/yarns.webapp/900-implementations.yarn b/yarns.webapp/900-implementations.yarn index 9310f6e..2d24c44 100644 --- a/yarns.webapp/900-implementations.yarn +++ b/yarns.webapp/900-implementations.yarn @@ -24,13 +24,14 @@ but the shell doesn't wait for it to terminate. This way, WEBAPP will be running until it crashes or is explicitly killed. IMPLEMENTS GIVEN a running WEBAPP - + rm -f "$DATADIR/webapp.pid" rm -f "$DATADIR/webapp.port" mkfifo "$DATADIR/webapp.port" start-stop-daemon -S -x "$SRCDIR/lorry-controller-webapp" \ -b -p "$DATADIR/webapp.pid" -m --verbose \ -- \ --statedb "$DATADIR/webapp.db" \ + --status-html "$DATADIR/lc-status.html" \ --log-level debug \ --log "$DATADIR/webapp.log" \ --debug-host 127.0.0.1 \ @@ -82,6 +83,10 @@ scenario doesn't need ot check that separately. rm -f "$DATADIR/response.headers" rm -f "$DATADIR/response.body" port=$(cat "$DATADIR/webapp.port") + + # The timestamp is needed by "THEN static status page got updated" + touch "$DATADIR/request.timestamp" + curl \ -D "$DATADIR/response.headers" \ -o "$DATADIR/response.body" \ @@ -99,20 +104,43 @@ Check the Content-Type of the response is JSON. A JSON response can then be queried further. The JSON is expected to be a dict, so that values are accessed by name from the dict. The -value is expresssed as a Python value in the step. +value is expresssed as a JSON value in the step. - IMPLEMENTS THEN response has (\S+) set to (\S+) + IMPLEMENTS THEN response has (\S+) set to (.+) cat "$DATADIR/response.body" - python -c " + python -c ' import json, os, sys data = json.load(sys.stdin) - key = os.environ['MATCH_1'] - expected = eval(os.environ['MATCH_2']) # I feel dirty and evil. + key = os.environ["MATCH_1"] + expected = json.loads(os.environ["MATCH_2"]) value = data[key] if value != expected: sys.stderr.write( - 'Key {key} has value {value}, but ' - '{expected} was expected'.format( + "Key {key} has value {value}, but " + "{expected} was expected".format ( key=key, value=value, expected=expected)) sys.exit(1) - " < "$DATADIR/response.body" + ' < "$DATADIR/response.body" + + +Status web page +--------------- + +WEBAPP is expected to update a static HTML pages whenever the +`/1.0/status` request is made. We configure WEBAPP to write it to +`$DATADIR/lc-status.html`. We don't test the contents of the page, but +we do test that it gets updated. We test for the updates by comparing +the modification time of the file with the time of the request. We +know the time of the request thanks to the "WHEN admin makes a +request" step updating the modification time of a file for this +purpose. + + IMPLEMENTS THEN static status page got updated + # test -nt isn't useful: the timestamps might be identical, and + # that's OK on filesystems that only store full-second timestamps. + # We generate timestamps in (roughly) ISO 8601 format, with stat, + # and those can be compared using simple string comparison. + + status=$(stat -c %y "$DATADIR/lc-status.html") + request=$(stat -c %y "$DATADIR/request.timestamp") + test "$request" = "$status" || test "$request" '<' "$status" |