diff options
-rw-r--r-- | ick2/controllerapi.py | 54 | ||||
-rw-r--r-- | ick2/controllerapi_tests.py | 6 | ||||
-rw-r--r-- | yarns/400-build.yarn | 46 |
3 files changed, 94 insertions, 12 deletions
diff --git a/ick2/controllerapi.py b/ick2/controllerapi.py index 5cf5d64..c72c300 100644 --- a/ick2/controllerapi.py +++ b/ick2/controllerapi.py @@ -34,6 +34,7 @@ class ControllerAPI: apis = { '/version': VersionAPI, '/builds': BuildsAPI, + '/log': LogAPI, '/projects': ProjectAPI, '/work': WorkAPI, '/workers': WorkerAPI, @@ -93,7 +94,11 @@ class APIbase: # pragma: no cover body = callback(**kwargs) except ick2.NotFound as e: return not_found(e) - return OK(body) + if isinstance(body, dict): + return OK(body) + elif isinstance(body, str): + return text_plain(body) + raise Exception('this must not happen') return wrapper def POST(self, callback): @@ -242,6 +247,26 @@ class BuildsAPI(ResourceApiBase): # pragma: no cover raise MethodNotAllowed('Updating builds directly is not allowed') +class LogAPI(ResourceApiBase): # pragma: no cover + + def __init__(self, state): + super().__init__('log', state) + + def get_resource_name(self, resource): + return resource['log'] + + def create(self, body): # pragma: no cover + raise MethodNotAllowed('Creating builds directly is not allowed') + + def update(self, body, name): # pragma: no cover + raise MethodNotAllowed('Updating builds directly is not allowed') + + def show(self, name): + log = self._state.get_resource('log', str(name)) + ick2.log.log('info', msg_text='Returning log', log=log) + return log['log'] + + class ProjectAPI(ResourceApiBase): def __init__(self, state): @@ -343,6 +368,7 @@ class WorkAPI(APIbase): self._update_project(project) build_id = self._start_build(project, pipeline, worker) + self._start_log(build_id) index = 0 doing = { @@ -352,6 +378,7 @@ class WorkAPI(APIbase): 'pipeline': pipeline['name'], 'step': pipeline['actions'][index], 'step_index': index, + 'log': '/log/{}'.format(build_id), } worker_state = { @@ -450,6 +477,7 @@ class WorkAPI(APIbase): build_id = 1 build = { 'build_id': build_id, + 'log': '/log/{}'.format(build_id), 'worker': worker, 'project': project['project'], 'pipeline': pipeline['name'], @@ -458,8 +486,23 @@ class WorkAPI(APIbase): self._state.add_resource('builds', str(build_id), build) return build_id + def _start_log(self, build_id): + ick2.log.log('info', msg_text='Starting new log', build_id=build_id) + log = { + 'build_id': build_id, + 'log': '', + } + self._state.add_resource('log', str(build_id), log) + return build_id + def _append_to_build_log(self, update): - pass + build_id = update['build_id'] + log = self._state.get_resource('log', str(build_id)) + for kind in ['stdout', 'stderr']: + text = update.get(kind, '') + if text is not None: + log['log'] += text + self._state.update_resource('log', str(build_id), log) def _finish_build(self, update): build = self._state.get_resource('builds', str(update['build_id'])) @@ -510,6 +553,13 @@ def OK(body): # pragma: no cover return response(apifw.HTTP_OK, body, headers) +def text_plain(body): # pragma: no cover + headers = { + 'Content-Type': 'text/plain', + } + return response(apifw.HTTP_OK, body, headers) + + def not_found(error): # pragma: no cover headers = { 'Content-Type': 'text/plain', diff --git a/ick2/controllerapi_tests.py b/ick2/controllerapi_tests.py index ce1e1d5..709f184 100644 --- a/ick2/controllerapi_tests.py +++ b/ick2/controllerapi_tests.py @@ -294,6 +294,7 @@ class WorkAPITests(unittest.TestCase): 'shell': 'step-1', }, 'step_index': 0, + 'log': '/log/1', } self.assertEqual(work.get_work('asterix'), expected) @@ -316,6 +317,7 @@ class WorkAPITests(unittest.TestCase): 'shell': 'step-1', }, 'step_index': 0, + 'log': '/log/1', } self.assertEqual(work.get_work('asterix'), expected) @@ -326,8 +328,8 @@ class WorkAPITests(unittest.TestCase): 'project': 'foo', 'pipeline': 'build', 'exit_code': None, - 'stdout': None, - 'stderr': None, + 'stdout': 'out', + 'stderr': 'err', 'timestamp': '2000-01-01T00:00:00', } work.update_work(done) diff --git a/yarns/400-build.yarn b/yarns/400-build.yarn index b544695..c57fd07 100644 --- a/yarns/400-build.yarn +++ b/yarns/400-build.yarn @@ -35,6 +35,7 @@ Set up the controller. ... uapi_workers_id_get ... uapi_builds_get ... uapi_builds_id_get + ... uapi_log_id_get AND a running ick controller Add up a project. @@ -94,6 +95,7 @@ be in the path or can we get it in the access token?** AND body matches ... { ... "build_id": 1, + ... "log": "/log/1", ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -108,6 +110,7 @@ be in the path or can we get it in the access token?** AND body matches ... { ... "build_id": 1, + ... "log": "/log/1", ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -133,6 +136,7 @@ User can now see pipeline is running and which worker is building it. ... "worker": "obelix", ... "doing": { ... "build_id": 1, + ... "log": "/log/1", ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -150,14 +154,21 @@ User can now see pipeline is running and which worker is building it. ... "builds": [ ... { ... "build_id": 1, + ... "log": "/log/1", ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", - ... "status": "building" + ... "status": "building", + ... "log": "/log/1" ... } ... ] ... } + WHEN user makes request GET /log/1 + THEN result has status code 200 + AND result has header Content-Type: text/plain + AND body text is "" + Worker reports some build output. Note the null exit code. WHEN worker-manager makes request POST /work @@ -167,7 +178,7 @@ Worker reports some build output. Note the null exit code. ... "project": "rome", ... "pipeline": "construct", ... "exit_code": null, - ... "stdout": "hey ho hey ho", + ... "stdout": "hey ho", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } @@ -180,6 +191,7 @@ Still the same job, since the first build step didnt't finish. AND body matches ... { ... "build_id": 1, + ... "log": "/log/1", ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -189,6 +201,13 @@ Still the same job, since the first build step didnt't finish. ... "step_index": 0 ... } +The build log is immediately accessible. + + WHEN user makes request GET /log/1 + THEN result has status code 200 + AND result has header Content-Type: text/plain + AND body text is "hey ho" + Report the step is done, and successfully. WHEN worker-manager makes request POST /work @@ -198,12 +217,17 @@ Report the step is done, and successfully. ... "project": "rome", ... "pipeline": "construct", ... "exit_code": 0, - ... "stdout": "hey ho, hey ho\n", + ... "stdout": ", hey ho\n", ... "stderr": "", ... "timestamp": "2017-10-27T17:08:49" ... } THEN result has status code 201 + WHEN user makes request GET /log/1 + THEN result has status code 200 + AND result has header Content-Type: text/plain + AND body text is "hey ho, hey ho\n" + Now there's another step to do. WHEN worker-manager makes request GET /work/obelix @@ -211,6 +235,7 @@ Now there's another step to do. AND body matches ... { ... "build_id": 1, + ... "log": "/log/1", ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -235,7 +260,8 @@ User sees changed status. ... "step_index": 1, ... "step": { ... "shell": "day 2" - ... } + ... }, + ... "log": "/log/1" ... } ... } @@ -275,10 +301,12 @@ Also, there's a build with a log. ... "builds": [ ... { ... "build_id": 1, + ... "log": "/log/1", ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", - ... "status": 0 + ... "status": 0, + ... "log": "/log/1" ... } ... ] ... } @@ -288,15 +316,17 @@ Also, there's a build with a log. AND body matches ... { ... "build_id": 1, + ... "log": "/log/1", ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", - ... "status": 0 + ... "status": 0, + ... "log": "/log/1" ... } - WHEN user makes request GET /builds/1/log + WHEN user makes request GET /log/1 THEN result has status code 200 AND result has header Content-Type: text/plain - AND body matches "hey ho, hey ho\nto the gold mine we go!\n" + AND body text is "hey ho, hey ho\nto the gold mine we go!\n" FINALLY stop ick controller |