diff options
author | Lars Wirzenius <liw@liw.fi> | 2017-11-26 18:03:15 +0100 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2017-11-26 18:03:15 +0100 |
commit | 7b06ae78e20717161467affb37b88857e4fc610b (patch) | |
tree | d33dbbb46479c5372b56386c88698e80e1c79823 | |
parent | db3e88505f0a6ad11519cf1615444b1d423d42fe (diff) | |
parent | 7a2829fe5457c05d1bd25fbba9bc7d16eb091b59 (diff) | |
download | ick2-7b06ae78e20717161467affb37b88857e4fc610b.tar.gz |
Merge branch 'liw/namedpipes'
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | ick2/__init__.py | 1 | ||||
-rw-r--r-- | ick2/apibase.py | 10 | ||||
-rw-r--r-- | ick2/controllerapi.py | 1 | ||||
-rw-r--r-- | ick2/pipelineapi.py | 25 | ||||
-rw-r--r-- | ick2/projectapi.py | 58 | ||||
-rw-r--r-- | ick2/projectapi_tests.py | 56 | ||||
-rw-r--r-- | ick2/workapi.py | 37 | ||||
-rw-r--r-- | ick2/workapi_tests.py | 25 | ||||
-rw-r--r-- | without-tests | 1 | ||||
-rw-r--r-- | yarns/100-projects.yarn | 105 | ||||
-rw-r--r-- | yarns/150-pipelines.yarn | 168 | ||||
-rw-r--r-- | yarns/400-build.yarn | 31 | ||||
-rw-r--r-- | yarns/500-build-fail.yarn | 22 |
14 files changed, 371 insertions, 173 deletions
@@ -24,6 +24,10 @@ Version 0.18+git, not yet released work steps that the worker-manager gets, include the parameters. Worker-manager does not do anything with them yet. +* Pipelines are now shared between projects, and projects refer to + them by name. Pipeline status is per project, however, so if two + project refer to the same pipeline, they can trigger it separately. + Version 0.18, released 2017-11-25 ---------------------------------- diff --git a/ick2/__init__.py b/ick2/__init__.py index 13912fe..9b4d9e3 100644 --- a/ick2/__init__.py +++ b/ick2/__init__.py @@ -34,6 +34,7 @@ from .apibase import APIbase, ResourceApiBase from .buildsapi import BuildsAPI from .logapi import LogAPI from .versionapi import VersionAPI +from .pipelineapi import PipelineAPI from .projectapi import ProjectAPI from .workapi import WorkAPI from .workerapi import WorkerAPI diff --git a/ick2/apibase.py b/ick2/apibase.py index 361baaa..1e4fd14 100644 --- a/ick2/apibase.py +++ b/ick2/apibase.py @@ -58,8 +58,10 @@ class APIbase: try: if 'raw_uri_path' in kwargs: del kwargs['raw_uri_path'] - body = callback(**kwargs) + body = callback(**kwargs) except ick2.NotFound as e: + ick2.log.log( + 'warning', msg_text='GET Not found', kwargs=kwargs) return ick2.not_found(e) if isinstance(body, dict): return ick2.OK(body) @@ -76,7 +78,7 @@ class APIbase: try: body = callback(body) except ick2.ExistsAlready as e: - ick2.log.log('error', msg_text=str(e)) + ick2.log.log('error', msg_text=str(e), kwargs=kwargs) return ick2.conflict(str(e)) return ick2.created(body) return wrapper @@ -91,6 +93,8 @@ class APIbase: try: body = callback(body, **kwargs) except ick2.NotFound as e: + ick2.log.log( + 'warning', msg_text='PUT Not found', kwargs=kwargs) return ick2.not_found(e) except ick2.WrongPipelineStatus as e: ick2.log.log( @@ -112,6 +116,8 @@ class APIbase: del kwargs['raw_uri_path'] body = callback(**kwargs) except ick2.NotFound as e: + ick2.log.log( + 'warning', msg_text='DELETE Not found', kwargs=kwargs) return ick2.not_found(e) return ick2.OK(body) return wrapper diff --git a/ick2/controllerapi.py b/ick2/controllerapi.py index 4b67d10..1ef71cc 100644 --- a/ick2/controllerapi.py +++ b/ick2/controllerapi.py @@ -32,6 +32,7 @@ class ControllerAPI: '/version': ick2.VersionAPI, '/builds': ick2.BuildsAPI, '/logs': ick2.LogAPI, + '/pipelines': ick2.PipelineAPI, '/projects': ick2.ProjectAPI, '/work': ick2.WorkAPI, '/workers': ick2.WorkerAPI, diff --git a/ick2/pipelineapi.py b/ick2/pipelineapi.py new file mode 100644 index 0000000..061505e --- /dev/null +++ b/ick2/pipelineapi.py @@ -0,0 +1,25 @@ +# Copyright (C) 2017 Lars Wirzenius +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +import ick2 + + +class PipelineAPI(ick2.ResourceApiBase): + + def __init__(self, state): + super().__init__('pipelines', state) + + def get_resource_name(self, resource): + return resource['name'] diff --git a/ick2/projectapi.py b/ick2/projectapi.py index b0c9da1..f46fe14 100644 --- a/ick2/projectapi.py +++ b/ick2/projectapi.py @@ -50,33 +50,59 @@ class ProjectAPI(ick2.ResourceApiBase): def get_pipeline(self, project, pipeline, **kwargs): p = self._state.get_resource(self._type_name, project) - for pl in p['pipelines']: - if pl['name'] == pipeline: - return { - 'status': pl.get('status', 'idle'), - } - raise ick2.NotFound() + if pipeline not in p['pipelines']: + raise ick2.NotFound() + + pp = self._pipeline_instance_name(project, pipeline) + try: + pl = self._state.get_resource('pipeline_instances', pp) + except ick2.NotFound: + pl = {} + return { + 'status': pl.get('status', 'idle'), + } + + def _pipeline_instance_name(self, project_name, pipeline_name): + return '{} {}'.format(project_name, pipeline_name) def set_pipeline_callback( self, body, project, pipeline, **kwargs): # pragma: no cover return self.set_pipeline(body['status'], project, pipeline) - def set_pipeline(self, state, project, pipeline): + def set_pipeline(self, status, project, pipeline): + ick2.log.log( + 'trace', msg_text='Setting pipeline status', + project=project, pipeline=pipeline, status=status) + allowed_changes = { 'idle': 'triggered', 'triggered': 'building', 'building': 'idle', } + p = self._state.get_resource(self._type_name, project) - for pl in p['pipelines']: - if pl['name'] == pipeline: - old_state = pl.get('status', 'idle') - if allowed_changes[old_state] != state: - raise ick2.WrongPipelineStatus(state) - pl['status'] = state - self._state.update_resource(self._type_name, project, p) - return {'status': state} - raise ick2.NotFound() + if pipeline not in p['pipelines']: + ick2.log.log( + 'error', msg_text='Project not found', project=project) + raise ick2.NotFound() + ick2.log.log('trace', msg_text='Found project', project=p) + + pp = self._pipeline_instance_name(project, pipeline) + try: + pl = self._state.get_resource('pipeline_instances', pp) + except ick2.NotFound: + pl = { + 'name': pipeline, + 'status': 'idle', + } + self._state.add_resource('pipeline_instances', pp, pl) + + old_status = pl.get('status', 'idle') + if allowed_changes[old_status] != status: + raise ick2.WrongPipelineStatus(status) + pl['status'] = status + self._state.update_resource('pipeline_instances', pp, pl) + return {'status': status} # This needs to go away as it is not protected. Once an IDP is # added. diff --git a/ick2/projectapi_tests.py b/ick2/projectapi_tests.py index ae5190e..4e55ccf 100644 --- a/ick2/projectapi_tests.py +++ b/ick2/projectapi_tests.py @@ -36,28 +36,32 @@ class ProjectAPITests(unittest.TestCase): def create_api(self): return ick2.ProjectAPI(self.state) + def create_pipeline_api(self): + return ick2.PipelineAPI(self.state) + def test_has_not_projects_initially(self): api = self.create_api() self.assertEqual(api.list(), {'projects': []}) def test_creates_project(self): + pipeline = { + 'name': 'build', + 'actions': [ + {'shell': 'step-1'}, + ], + } + pipeapi = self.create_pipeline_api() + pipeapi.create(pipeline) + project = { 'project': 'foo', - 'pipelines': [ - { - 'name': 'build', - 'actions': [ - { - 'shell': 'step-1', - }, - ], - }, - ], + 'pipelines': ['build'], 'parameters': { 'foo': 'bar', } } api = self.create_api() + self.assertEqual(api.create(project), project) self.assertEqual(api.list(), {'projects': [project]}) self.assertEqual(api.get_pipeline('foo', 'build'), {'status': 'idle'}) @@ -85,7 +89,7 @@ class ProjectAPITests(unittest.TestCase): def test_loads_projects_from_state_directory(self): project = { 'project': 'foo', - 'shell_steps': ['build'], + 'pipelines': ['build'], } api = self.create_api() api.create(project) @@ -96,7 +100,7 @@ class ProjectAPITests(unittest.TestCase): def test_gets_named_project(self): project = { 'project': 'foo', - 'shell_steps': ['build'], + 'pipelines': ['build'], } api = self.create_api() api.create(project) @@ -105,7 +109,7 @@ class ProjectAPITests(unittest.TestCase): def test_updates_named_project(self): project_v1 = { 'project': 'foo', - 'shell_steps': ['build'], + 'pipelines': ['build'], } project_v2 = dict(project_v1) project_v2['shell_steps'] = ['build it using magic'] @@ -118,7 +122,7 @@ class ProjectAPITests(unittest.TestCase): def test_deletes_named_project(self): project = { 'project': 'foo', - 'shell_steps': ['build'], + 'pipelines': ['build'], } api = self.create_api() api.create(project) @@ -133,27 +137,29 @@ class ProjectAPITests(unittest.TestCase): api.delete('foo') def test_updates_pipeline_status(self): + pipeline = { + 'name': 'build', + 'actions': [ + {'shell': 'step-1'}, + ], + } + pipeapi = self.create_pipeline_api() + pipeapi.create(pipeline) + project = { 'project': 'foo', - 'pipelines': [ - { - 'name': 'build', - 'actions': [ - { - 'shell': 'step-1', - }, - ], - }, - ], + 'pipelines': ['build'], } api = self.create_api() api.create(project) self.assertEqual(api.get_pipeline('foo', 'build'), {'status': 'idle'}) + self.assertEqual(pipeapi.show('build'), pipeline) with self.assertRaises(ick2.WrongPipelineStatus): api.set_pipeline('building', 'foo', 'build') api.set_pipeline('triggered', 'foo', 'build') + self.assertEqual(pipeapi.show('build'), pipeline) self.assertEqual( api.get_pipeline('foo', 'build'), {'status': 'triggered'} @@ -163,6 +169,7 @@ class ProjectAPITests(unittest.TestCase): api.set_pipeline('idle', 'foo', 'build') api.set_pipeline('building', 'foo', 'build') + self.assertEqual(pipeapi.show('build'), pipeline) self.assertEqual( api.get_pipeline('foo', 'build'), {'status': 'building'} @@ -172,6 +179,7 @@ class ProjectAPITests(unittest.TestCase): api.set_pipeline('triggered', 'foo', 'build') api.set_pipeline('idle', 'foo', 'build') + self.assertEqual(pipeapi.show('build'), pipeline) self.assertEqual( api.get_pipeline('foo', 'build'), {'status': 'idle'} diff --git a/ick2/workapi.py b/ick2/workapi.py index 5935fe9..f84e355 100644 --- a/ick2/workapi.py +++ b/ick2/workapi.py @@ -46,7 +46,7 @@ class WorkAPI(ick2.APIbase): doing = {} else: pipeline['status'] = 'building' - self._projects.update_project(project) + self._update_pipeline(project, pipeline) build_id = project.get('build_id', 0) + 1 project['build_id'] = build_id @@ -78,11 +78,30 @@ class WorkAPI(ick2.APIbase): def _pick_triggered_pipeline(self): projects = self._projects.get_projects() for project in projects: - for pipeline in project['pipelines']: - if pipeline.get('status') == 'triggered': - return project, pipeline + for name in project['pipelines']: + pp = self._pipeline_instance_name(project['project'], name) + try: + pl = self._state.get_resource('pipeline_instances', pp) + except ick2.NotFound: + pass + else: + if pl.get('status') == 'triggered': + pt = self._state.get_resource('pipelines', name) + return project, pt return None, None + def _pipeline_instance_name(self, project_name, pipeline_name): + return '{} {}'.format(project_name, pipeline_name) + + def _update_pipeline(self, project, pipeline): + pp = self._pipeline_instance_name(project['project'], pipeline['name']) + try: + self._state.get_resource('pipeline_instances', pp) + except ick2.NotFound: # pragma: no cover + self._state.add_resource('pipeline_instances', pp, pipeline) + else: + self._state.update_resource('pipeline_instances', pp, pipeline) + def _start_build(self, project, pipeline, worker, build_id): ick2.log.log('info', msg_text='Starting new build', build_id=build_id) build = { @@ -136,6 +155,7 @@ class WorkAPI(ick2.APIbase): doing['step_index'] = index doing['step'] = actions[index] self._projects.update_project(project) + self._update_pipeline(project, pipeline) worker_state = { 'worker': update['worker'], @@ -146,7 +166,7 @@ class WorkAPI(ick2.APIbase): assert isinstance(exit_code, int) assert exit_code != 0 pipeline['status'] = 'idle' - self._projects.update_project(project) + self._update_pipeline(project, pipeline) self._finish_build(update) worker_state = { @@ -168,9 +188,10 @@ class WorkAPI(ick2.APIbase): def _get_pipeline(self, project, pipeline): # pragma: no cover projects = self._projects.get_projects() for p in projects: - for pl in p['pipelines']: - if pl.get('name') == pipeline: - return p, pl + for name in p['pipelines']: + if name == pipeline: + pt = self._state.get_resource('pipelines', name) + return p, pt raise ick2.NotFound() def _append_to_build_log(self, update): diff --git a/ick2/workapi_tests.py b/ick2/workapi_tests.py index f657687..c855895 100644 --- a/ick2/workapi_tests.py +++ b/ick2/workapi_tests.py @@ -34,24 +34,23 @@ class WorkAPITests(unittest.TestCase): shutil.rmtree(self.tempdir) def create_project_api(self): + pipeline = { + 'name': 'build', + 'actions': [ + {'shell': 'step-1'}, + {'shell': 'step-2'}, + ], + } + + pipeapi = ick2.PipelineAPI(self.state) + pipeapi.create(pipeline) + project = { 'project': 'foo', 'parameters': { 'foo': 'bar', }, - 'pipelines': [ - { - 'name': 'build', - 'actions': [ - { - 'shell': 'step-1', - }, - { - 'shell': 'step-2', - }, - ], - }, - ], + 'pipelines': ['build'], } api = ick2.ProjectAPI(self.state) api.create(project) diff --git a/without-tests b/without-tests index b587c70..6b58af3 100644 --- a/without-tests +++ b/without-tests @@ -4,6 +4,7 @@ ick2/buildsapi.py ick2/exceptions.py ick2/logapi.py ick2/logging.py +ick2/pipelineapi.py ick2/responses.py ick2/version.py ick2/workerapi.py diff --git a/yarns/100-projects.yarn b/yarns/100-projects.yarn index aa04b7a..ba723d3 100644 --- a/yarns/100-projects.yarn +++ b/yarns/100-projects.yarn @@ -27,24 +27,9 @@ like this: { "project": "ick2-website", "pipelines": [ - { - "name": "setup-workspace", - "actions": [ - { "shell": "git clone git://git.liw.fi/ick2-website src" } - ] - }, - { - "name": "build-website", - "actions": [ - { "shell": "cd src && ikiwiki --setup ikiwiki.setup" } - ] - }, - { - "name": "publish-website", - "actions": [ - { "shell": "cd html && rsync -a --delete . server::/srv/http/ick2/." } - ] - } + "setup-workspace", + "build-website", + "publish-website" ] } @@ -63,6 +48,7 @@ building them. We start by starting an instance of the controller. SCENARIO managing projects GIVEN an RSA key pair for token signing AND an access token for user with scopes + ... uapi_pipelines_post ... uapi_projects_get ... uapi_projects_post ... uapi_projects_id_get @@ -75,34 +61,25 @@ building them. We start by starting an instance of the controller. THEN result has status code 200 AND body matches { "projects": [] } + WHEN user makes request POST /pipelines with a valid token and body + ... { + ... "name": "build", + ... "actions": [ + ... { "shell": "git clone git://repo src" }, + ... { "shell": "mkdir html" }, + ... { "shell": "ikiwiki src html" } + ... ] + ... } WHEN user makes request POST /projects with a valid token and body ... { ... "project": "website", - ... "pipelines": [ - ... { - ... "name": "build", - ... "actions": [ - ... { "shell": "git clone git://repo src" }, - ... { "shell": "mkdir html" }, - ... { "shell": "ikiwiki src html" } - ... ] - ... } - ... ] + ... "pipelines": ["build"] ... } THEN result has status code 201 AND body matches ... { ... "project": "website", - ... "pipelines": [ - ... { - ... "name": "build", - ... "actions": [ - ... { "shell": "git clone git://repo src" }, - ... { "shell": "mkdir html" }, - ... { "shell": "ikiwiki src html" } - ... ] - ... } - ... ] + ... "pipelines": ["build"] ... } AND controller state directory contains project website @@ -122,16 +99,7 @@ Creating a new project with the same name is forbidden. ... "projects": [ ... { ... "project": "website", - ... "pipelines": [ - ... { - ... "name": "build", - ... "actions": [ - ... { "shell": "git clone git://repo src" }, - ... { "shell": "mkdir html" }, - ... { "shell": "ikiwiki src html" } - ... ] - ... } - ... ] + ... "pipelines": ["build"] ... } ... ] ... } @@ -143,43 +111,22 @@ Creating a new project with the same name is forbidden. AND body matches ... { ... "project": "website", - ... "pipelines": [ - ... { - ... "name": "build", - ... "actions": [ - ... { "shell": "git clone git://repo src" }, - ... { "shell": "mkdir html" }, - ... { "shell": "ikiwiki src html" } - ... ] - ... } - ... ] + ... "pipelines": ["build"] ... } WHEN user makes request PUT /projects/website with a valid token ... and body ... { ... "project": "website", - ... "pipelines": [ - ... { - ... "name": "build", - ... "actions": [ - ... { "shell": "build-it" } - ... ] - ... } - ... ] + ... "parameters": {"foo": "bar"}, + ... "pipelines": ["build"] ... } THEN result has status code 200 AND body matches ... { ... "project": "website", - ... "pipelines": [ - ... { - ... "name": "build", - ... "actions": [ - ... { "shell": "build-it" } - ... ] - ... } - ... ] + ... "parameters": {"foo": "bar"}, + ... "pipelines": ["build"] ... } AND controller state directory contains project website @@ -188,14 +135,8 @@ Creating a new project with the same name is forbidden. AND body matches ... { ... "project": "website", - ... "pipelines": [ - ... { - ... "name": "build", - ... "actions": [ - ... { "shell": "build-it" } - ... ] - ... } - ... ] + ... "parameters": {"foo": "bar"}, + ... "pipelines": ["build"] ... } WHEN user makes request DELETE /projects/website diff --git a/yarns/150-pipelines.yarn b/yarns/150-pipelines.yarn new file mode 100644 index 0000000..f0f678e --- /dev/null +++ b/yarns/150-pipelines.yarn @@ -0,0 +1,168 @@ +<!-- + +Copyright 2017 Lars Wirzenius + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. + +--> + +# Controller pipeline management + +The Ick2 controller manages information about named pipelines. +Pipelines are sequences of steps to achieve part of a project build. +They're described like resources like this: + + EXAMPLE pipeline resource + { + "name": "build-website", + "parameters": { + "foo": "bar" + }, + "actions": [ + { + "shell": "git clone git://git.liw.fi/ick2-website src" } + }, + { + "shell": "cd src && ikiwiki --setup ikiwiki.setup" + }, + { + "shell": "cd html && rsync -a --delete . server::/srv/http/ick2/." + } + ] + } + +In other words, there are several things that define a pipeline: + +* The `name`. This is used for referreing to the pipeline in the API. +* A set of parameters, which are currently ignored. +* A sequence of actions. At the moment, each action is a shell + command to be run, but that will change later. + +## Managing pipelines + +First we test the controller API for managing pipelines, without +running them. We start by starting an instance of the controller. + + SCENARIO managing pipelines + GIVEN an RSA key pair for token signing + AND an access token for user with scopes + ... uapi_pipelines_get + ... uapi_pipelines_post + ... uapi_pipelines_id_get + ... uapi_pipelines_id_put + ... uapi_pipelines_id_delete + AND controller config uses statedir at the state directory + AND a running ick controller + + WHEN user makes request GET /pipelines + THEN result has status code 200 + AND body matches { "pipelines": [] } + + WHEN user makes request POST /pipelines with a valid token and body + ... { + ... "name": "build_website", + ... "actions": [ + ... { "shell": "git clone git://repo src" }, + ... { "shell": "mkdir html" }, + ... { "shell": "ikiwiki src html" } + ... ] + ... } + THEN result has status code 201 + AND body matches + ... { + ... "name": "build_website", + ... "actions": [ + ... { "shell": "git clone git://repo src" }, + ... { "shell": "mkdir html" }, + ... { "shell": "ikiwiki src html" } + ... ] + ... } + +Creating a new pipeline with the same name is forbidden. + + WHEN user makes request POST /pipelines with a valid token and body + ... { + ... "name": "build_website" + ... } + THEN result has status code 409 + + WHEN user makes request GET /pipelines + THEN result has status code 200 + AND body matches + ... { + ... "pipelines": [ + ... { + ... "name": "build_website", + ... "actions": [ + ... { "shell": "git clone git://repo src" }, + ... { "shell": "mkdir html" }, + ... { "shell": "ikiwiki src html" } + ... ] + ... } + ... ] + ... } + + WHEN user stops ick controller + GIVEN a running ick controller + WHEN user makes request GET /pipelines/build_website + THEN result has status code 200 + AND body matches + ... { + ... "name": "build_website", + ... "actions": [ + ... { "shell": "git clone git://repo src" }, + ... { "shell": "mkdir html" }, + ... { "shell": "ikiwiki src html" } + ... ] + ... } + + WHEN user makes request PUT /pipelines/build_websitte with a valid token + ... and body + ... { + ... "name": "build_website", + ... "actions": [ + ... { "shell": "build-it" } + ... ] + ... } + THEN result has status code 200 + AND body matches + ... { + ... "name": "build_website", + ... "actions": [ + ... { "shell": "build-it" } + ... ] + ... } + + WHEN user makes request GET /pipelines/build_website + THEN result has status code 200 + AND body matches + ... { + ... "name": "build_website", + ... "actions": [ + ... { "shell": "build-it" } + ... ] + ... } + + WHEN user makes request DELETE /pipelines/build_website + THEN result has status code 200 + WHEN user makes request GET /pipelines/build_website + THEN result has status code 404 + + WHEN user makes request PUT /pipelines/doesnotexist with a valid token and body + ... { + ... "name": "doesnotexist" + ... } + THEN result has status code 404 + + FINALLY stop ick controller diff --git a/yarns/400-build.yarn b/yarns/400-build.yarn index a07eda4..ec3e8e5 100644 --- a/yarns/400-build.yarn +++ b/yarns/400-build.yarn @@ -28,6 +28,7 @@ Set up the controller. GIVEN an RSA key pair for token signing AND controller config uses statedir at the state directory AND an access token for user with scopes + ... uapi_pipelines_post ... uapi_projects_post ... uapi_projects_id_pipelines_id_put ... uapi_projects_id_pipelines_id_get @@ -38,23 +39,24 @@ Set up the controller. ... uapi_logs_id_get AND a running ick controller -Add up a project. +Add up a project with some named pipelines. + WHEN user makes request POST /pipelines with a valid token and body + ... { + ... "name": "construct", + ... "actions": [ + ... { "shell": "day 1" }, + ... { "shell": "day 2" } + ... ] + ... } + THEN result has status code 201 WHEN user makes request POST /projects with a valid token and body ... { ... "project": "rome", ... "parameters": { ... "foo": "bar" ... }, - ... "pipelines": [ - ... { - ... "name": "construct", - ... "actions": [ - ... { "shell": "day 1" }, - ... { "shell": "day 2" } - ... ] - ... } - ... ] + ... "pipelines": ["construct"] ... } THEN result has status code 201 @@ -66,14 +68,7 @@ Add a second project so we know each project gets its own work steps. ... "parameters": { ... "hey": "there" ... }, - ... "pipelines": [ - ... { - ... "name": "construct", - ... "actions": [ - ... { "shell": "fork" } - ... ] - ... } - ... ] + ... "pipelines": ["construct"] ... } THEN result has status code 201 diff --git a/yarns/500-build-fail.yarn b/yarns/500-build-fail.yarn index 2f5d79c..f0afa32 100644 --- a/yarns/500-build-fail.yarn +++ b/yarns/500-build-fail.yarn @@ -29,6 +29,7 @@ Set up the controller. GIVEN an RSA key pair for token signing AND controller config uses statedir at the state directory AND an access token for user with scopes + ... uapi_pipelines_post ... uapi_projects_post ... uapi_projects_id_pipelines_id_put ... uapi_projects_id_pipelines_id_get @@ -39,20 +40,21 @@ Set up the controller. ... uapi_logs_id_get AND a running ick controller -Add up a project. +Add up a project and its pipelines. + WHEN user makes request POST /pipelines with a valid token and body + ... { + ... "name": "construct", + ... "actions": [ + ... { "shell": "day 1" }, + ... { "shell": "day 2" } + ... ] + ... } + THEN result has status code 201 WHEN user makes request POST /projects with a valid token and body ... { ... "project": "rome", - ... "pipelines": [ - ... { - ... "name": "construct", - ... "actions": [ - ... { "shell": "day 1" }, - ... { "shell": "day 2" } - ... ] - ... } - ... ] + ... "pipelines": ["construct"] ... } THEN result has status code 201 |