summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2017-11-05 20:15:58 +0100
committerLars Wirzenius <liw@liw.fi>2017-11-05 20:15:58 +0100
commitce63c337d6f8357367efd730c373cd033a568181 (patch)
treef352e647de4bab9006c7ff2a1a47a2136b715ca1
parent30508ca876f00d5aae004ca0588258767e345480 (diff)
downloadick2-ce63c337d6f8357367efd730c373cd033a568181.tar.gz
Add: advance to next step in pipeline automatically
-rw-r--r--ick2/controllerapi.py61
-rw-r--r--ick2/controllerapi_tests.py59
-rw-r--r--yarns/400-build.yarn2
3 files changed, 120 insertions, 2 deletions
diff --git a/ick2/controllerapi.py b/ick2/controllerapi.py
index a7d4026..b140ce5 100644
--- a/ick2/controllerapi.py
+++ b/ick2/controllerapi.py
@@ -323,11 +323,13 @@ class WorkAPI(APIbase):
pipeline['status'] = 'building'
self._update_project(project)
+ index = 0
doing = {
+ 'worker': worker,
'project': project['project'],
'pipeline': pipeline['name'],
- 'step': pipeline['actions'][0],
- 'step_index': 0,
+ 'step': pipeline['actions'][index],
+ 'step_index': index,
}
worker_state = {
@@ -364,6 +366,55 @@ class WorkAPI(APIbase):
def _update_project(self, project):
self._state.update_resource('projects', project['project'], project)
+ def update_work(self, update):
+ if 'worker' not in update: # pragma: no cover
+ raise BadUpdate('no worker specified')
+
+ worker_state = self._get_worker(update['worker'])
+ doing = worker_state.get('doing', {})
+ self._check_work_update(doing, update)
+
+ project, pipeline = self._get_pipeline(
+ update['project'], update['pipeline'])
+
+ if update.get('exit_code') == 0:
+ index = doing['step_index'] + 1
+ actions = pipeline['actions']
+ if index >= len(actions):
+ self._finish_pipeline(project, pipeline)
+ doing = {}
+ else:
+ doing['step_index'] = index
+ doing['step'] = actions[index]
+
+ worker_state = {
+ 'worker': update['worker'],
+ 'doing': doing,
+ }
+ self._update_worker(worker_state)
+
+ def _check_work_update(self, doing, update): # pragma: no cover
+ must_match = ['worker', 'project', 'pipeline']
+ for name in must_match:
+ if name not in update:
+ raise BadUpdate('{} not specified'.format(name))
+ if doing.get(name) != update[name]:
+ raise BadUpdate(
+ '{} differs from current work: {} vs {}'.format(
+ name, doing.get(name), update[name]))
+
+ def _get_pipeline(self, project, pipeline): # pragma: no cover
+ projects = self._get_projects()
+ for p in projects:
+ for pl in p['pipelines']:
+ if pl.get('name') == pipeline:
+ return p, pl
+ raise ick2.NotFound()
+
+ def _finish_pipeline(self, project, pipeline):
+ pipeline['status'] = 'idle'
+ self._update_project(project)
+
def create(self, *args, **kwargs): # pragma: no cover
pass
@@ -380,6 +431,12 @@ class WorkAPI(APIbase):
pass
+class BadUpdate(Exception): # pragma: no cover
+
+ def __init__(self, how):
+ super().__init__('Work update is BAD: {}'.format(how))
+
+
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 6b31117..efada2a 100644
--- a/ick2/controllerapi_tests.py
+++ b/ick2/controllerapi_tests.py
@@ -252,6 +252,9 @@ class WorkAPITests(unittest.TestCase):
{
'shell': 'step-1',
},
+ {
+ 'shell': 'step-2',
+ },
],
},
],
@@ -283,6 +286,7 @@ class WorkAPITests(unittest.TestCase):
self.create_worker_api()
work = self.create_work_api()
expected = {
+ 'worker': 'asterix',
'project': 'foo',
'pipeline': 'build',
'step': {
@@ -294,3 +298,58 @@ class WorkAPITests(unittest.TestCase):
# Check we get the same thing twice.
self.assertEqual(work.get_work('asterix'), expected)
+
+ def test_worker_manager_posts_work_updates(self):
+ projects = self.create_project_api()
+ projects.set_pipeline('triggered', 'foo', 'build')
+ self.create_worker_api()
+ work = self.create_work_api()
+
+ # Ask for some work.
+ expected = {
+ 'worker': 'asterix',
+ 'project': 'foo',
+ 'pipeline': 'build',
+ 'step': {
+ 'shell': 'step-1',
+ },
+ 'step_index': 0,
+ }
+ self.assertEqual(work.get_work('asterix'), expected)
+
+ # Post a partial update.
+ done = {
+ 'worker': 'asterix',
+ 'project': 'foo',
+ 'pipeline': 'build',
+ 'exit_code': None,
+ 'stdout': None,
+ 'stderr': None,
+ 'timestamp': '2000-01-01T00:00:00',
+ }
+ work.update_work(done)
+
+ # Ask for work again. We didn't finish the previous step, so
+ # should get same thing.
+ self.assertEqual(work.get_work('asterix'), expected)
+
+ # Finish the step.
+ done['exit_code'] = 0
+ work.update_work(done)
+
+ # We should get the next step now.
+ expected['step'] = {'shell': 'step-2'}
+ expected['step_index'] = 1
+ self.assertEqual(work.get_work('asterix'), expected)
+
+ # Finish the step.
+ done['exit_code'] = 0
+ work.update_work(done)
+
+ # We now get nothing further to do.
+ self.assertEqual(work.get_work('asterix'), {})
+
+ # An pipeline status has changed.
+ self.assertEqual(
+ projects.get_pipeline('foo', 'build'),
+ {'status': 'idle'})
diff --git a/yarns/400-build.yarn b/yarns/400-build.yarn
index 719ef05..7632f00 100644
--- a/yarns/400-build.yarn
+++ b/yarns/400-build.yarn
@@ -90,6 +90,7 @@ be in the path or can we get it in the access token?**
THEN result has status code 200
AND body matches
... {
+ ... "worker": "obelix",
... "project": "rome",
... "pipeline": "construct",
... "step": {
@@ -102,6 +103,7 @@ be in the path or can we get it in the access token?**
THEN result has status code 200
AND body matches
... {
+ ... "worker": "obelix",
... "project": "rome",
... "pipeline": "construct",
... "step": {