diff options
author | Lars Wirzenius <liw@liw.fi> | 2017-11-06 12:02:03 +0100 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2017-11-06 12:02:03 +0100 |
commit | 599e095cda626ff66c789017a752d7f91678d033 (patch) | |
tree | 24202f10b62992c5e0c1864c7e796e308b0a3cb8 | |
parent | 854bf88c1a69666db4aacfe540e9797ba6d6a69e (diff) | |
download | ick2-599e095cda626ff66c789017a752d7f91678d033.tar.gz |
Add: /builds
-rw-r--r-- | ick2/controllerapi.py | 50 | ||||
-rw-r--r-- | ick2/controllerapi_tests.py | 3 | ||||
-rw-r--r-- | yarns/400-build.yarn | 52 |
3 files changed, 96 insertions, 9 deletions
diff --git a/ick2/controllerapi.py b/ick2/controllerapi.py index a7d5fb5..5cf5d64 100644 --- a/ick2/controllerapi.py +++ b/ick2/controllerapi.py @@ -33,6 +33,7 @@ class ControllerAPI: def find_missing_route(self, missing_path): # pragma: no cover apis = { '/version': VersionAPI, + '/builds': BuildsAPI, '/projects': ProjectAPI, '/work': WorkAPI, '/workers': WorkerAPI, @@ -226,6 +227,21 @@ class WorkerAPI(ResourceApiBase): # pragma: no cover return resource['worker'] +class BuildsAPI(ResourceApiBase): # pragma: no cover + + def __init__(self, state): + super().__init__('builds', state) + + def get_resource_name(self, resource): + return resource['build'] + + 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') + + class ProjectAPI(ResourceApiBase): def __init__(self, state): @@ -326,8 +342,11 @@ class WorkAPI(APIbase): pipeline['status'] = 'building' self._update_project(project) + build_id = self._start_build(project, pipeline, worker) + index = 0 doing = { + 'build_id': build_id, 'worker': worker, 'project': project['project'], 'pipeline': pipeline['name'], @@ -379,6 +398,7 @@ class WorkAPI(APIbase): project, pipeline = self._get_pipeline( update['project'], update['pipeline']) + self._append_to_build_log(update) ick2.log.log( 'trace', @@ -395,6 +415,7 @@ class WorkAPI(APIbase): if index >= len(actions): pipeline['status'] = 'idle' doing = {} + self._finish_build(update) else: doing['step_index'] = index doing['step'] = actions[index] @@ -407,7 +428,7 @@ class WorkAPI(APIbase): self._update_worker(worker_state) def _check_work_update(self, doing, update): # pragma: no cover - must_match = ['worker', 'project', 'pipeline'] + must_match = ['worker', 'project', 'pipeline', 'build_id'] for name in must_match: if name not in update: raise BadUpdate('{} not specified'.format(name)) @@ -424,6 +445,27 @@ class WorkAPI(APIbase): return p, pl raise ick2.NotFound() + def _start_build(self, project, pipeline, worker): + ick2.log.log('info', msg_text='Starting new build') + build_id = 1 + build = { + 'build_id': build_id, + 'worker': worker, + 'project': project['project'], + 'pipeline': pipeline['name'], + 'status': 'building', + } + self._state.add_resource('builds', str(build_id), build) + return build_id + + def _append_to_build_log(self, update): + pass + + def _finish_build(self, update): + build = self._state.get_resource('builds', str(update['build_id'])) + build['status'] = update['exit_code'] + self._state.update_resource('builds', str(update['build_id']), build) + def create(self, *args, **kwargs): # pragma: no cover pass @@ -446,6 +488,12 @@ class BadUpdate(Exception): # pragma: no cover super().__init__('Work update is BAD: {}'.format(how)) +class MethodNotAllowed(Exception): # pragma: no cover + + def __init__(self, wat): + super().__init__(wat) + + def response(status_code, body, headers): # pragma: no cover obj = { 'status': status_code, diff --git a/ick2/controllerapi_tests.py b/ick2/controllerapi_tests.py index efada2a..ce1e1d5 100644 --- a/ick2/controllerapi_tests.py +++ b/ick2/controllerapi_tests.py @@ -286,6 +286,7 @@ class WorkAPITests(unittest.TestCase): self.create_worker_api() work = self.create_work_api() expected = { + 'build_id': 1, 'worker': 'asterix', 'project': 'foo', 'pipeline': 'build', @@ -307,6 +308,7 @@ class WorkAPITests(unittest.TestCase): # Ask for some work. expected = { + 'build_id': 1, 'worker': 'asterix', 'project': 'foo', 'pipeline': 'build', @@ -319,6 +321,7 @@ class WorkAPITests(unittest.TestCase): # Post a partial update. done = { + 'build_id': 1, 'worker': 'asterix', 'project': 'foo', 'pipeline': 'build', diff --git a/yarns/400-build.yarn b/yarns/400-build.yarn index 81124a4..b544695 100644 --- a/yarns/400-build.yarn +++ b/yarns/400-build.yarn @@ -33,6 +33,8 @@ Set up the controller. ... uapi_projects_id_pipeline_id_get ... uapi_projects_id_builds_get ... uapi_workers_id_get + ... uapi_builds_get + ... uapi_builds_id_get AND a running ick controller Add up a project. @@ -58,9 +60,9 @@ There are no builds for the project yet, and is idle. THEN result has status code 200 AND body matches { "status": "idle" } - WHEN user makes request GET /projects/rome/builds + WHEN user makes request GET /builds THEN result has status code 200 - AND body matches { "project": "rome", "builds": []} + AND body matches { "builds": [] } Register a worker. @@ -91,6 +93,7 @@ be in the path or can we get it in the access token?** THEN result has status code 200 AND body matches ... { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -104,6 +107,7 @@ be in the path or can we get it in the access token?** THEN result has status code 200 AND body matches ... { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -128,6 +132,7 @@ User can now see pipeline is running and which worker is building it. ... { ... "worker": "obelix", ... "doing": { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -138,11 +143,26 @@ User can now see pipeline is running and which worker is building it. ... } ... } + WHEN user makes request GET /builds + THEN result has status code 200 + AND body matches + ... { + ... "builds": [ + ... { + ... "build_id": 1, + ... "worker": "obelix", + ... "project": "rome", + ... "pipeline": "construct", + ... "status": "building" + ... } + ... ] + ... } Worker reports some build output. Note the null exit code. WHEN worker-manager makes request POST /work ... { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -159,6 +179,7 @@ Still the same job, since the first build step didnt't finish. THEN result has status code 200 AND body matches ... { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -172,6 +193,7 @@ Report the step is done, and successfully. WHEN worker-manager makes request POST /work ... { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -188,6 +210,7 @@ Now there's another step to do. THEN result has status code 200 AND body matches ... { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -205,6 +228,7 @@ User sees changed status. ... { ... "worker": "obelix", ... "doing": { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -219,6 +243,7 @@ Report it done. WHEN worker-manager makes request POST /work ... { + ... "build_id": 1, ... "worker": "obelix", ... "project": "rome", ... "pipeline": "construct", @@ -243,22 +268,33 @@ The pipeline status indicates success. Also, there's a build with a log. - WHEN user makes request GET /projects/rome/builds + WHEN user makes request GET /builds THEN result has status code 200 AND body matches ... { - ... "project": "rome", ... "builds": [ ... { + ... "build_id": 1, + ... "worker": "obelix", + ... "project": "rome", ... "pipeline": "construct", - ... "build_number": 1, - ... "status": "success", - ... "log": "/projects/rome/logs/1" + ... "status": 0 ... } ... ] ... } - WHEN user makes request GET /projects/rome/logs/1 + WHEN user makes request GET /builds/1 + THEN result has status code 200 + AND body matches + ... { + ... "build_id": 1, + ... "worker": "obelix", + ... "project": "rome", + ... "pipeline": "construct", + ... "status": 0 + ... } + + WHEN user makes request GET /builds/1/log 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" |