summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcheck2
-rwxr-xr-xlorry-controller-webapp51
-rw-r--r--yarns.webapp/020-status.yarn3
-rw-r--r--yarns.webapp/030-queue-management.yarn8
-rw-r--r--yarns.webapp/900-implementations.yarn46
5 files changed, 89 insertions, 21 deletions
diff --git a/check b/check
index a82754e..70aa608 100755
--- a/check
+++ b/check
@@ -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"