Migrating trigger functionality
Migrating trigger functionality to work with the app and workflow resources instead of the plan and pipeline resources. Change-Id: I5674c6dfc4e7d7813480cb8c9108012a5838dd22 Closes-Bug: #1495693
This commit is contained in:
parent
4d469df2d9
commit
706b1e1253
|
@ -16,6 +16,8 @@ import requests
|
|||
import yaml
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.common import apputils
|
||||
|
||||
|
||||
assembly_data = {'name': 'test_assembly',
|
||||
'description': 'desc assembly'}
|
||||
|
@ -53,8 +55,15 @@ class TestTriggerController(base.TestCase):
|
|||
self.assertIsNotNone(trigger_uri)
|
||||
return uuid, trigger_uri
|
||||
|
||||
def _create_app(self):
|
||||
data = apputils.get_sample_data()
|
||||
resp = self.client.create_app(data=data)
|
||||
bdy = json.loads(resp.body)
|
||||
trigger_uri = bdy['trigger_uri']
|
||||
return trigger_uri
|
||||
|
||||
def test_trigger_post(self):
|
||||
assembly_uuid, plan_uuid, trigger_uri = self._create_assembly()
|
||||
trigger_uri = self._create_app()
|
||||
# Using requests instead of self.client to test unauthenticated request
|
||||
status_url = 'https://api.github.com/repos/u/r/statuses/{sha}'
|
||||
body_dict = {'sender': {'url': 'https://api.github.com'},
|
||||
|
@ -64,8 +73,6 @@ class TestTriggerController(base.TestCase):
|
|||
resp = requests.post(trigger_uri, data=body)
|
||||
self.assertEqual(resp.status_code, 202)
|
||||
|
||||
self._delete_assembly(assembly_uuid, plan_uuid)
|
||||
|
||||
def test_trigger_post_with_empty_body(self):
|
||||
assembly_uuid, plan_uuid, trigger_uri = self._create_assembly()
|
||||
# Using requests instead of self.client to test unauthenticated request
|
||||
|
|
|
@ -19,30 +19,11 @@ from tempest_lib import exceptions as tempest_exceptions
|
|||
import yaml
|
||||
|
||||
from functionaltests.api import base
|
||||
from functionaltests.api.common import apputils
|
||||
|
||||
|
||||
class TestAppController(base.TestCase):
|
||||
|
||||
def _get_sample_data(self):
|
||||
data = dict()
|
||||
data["name"] = "test_app_1"
|
||||
data["description"] = "descp"
|
||||
data["languagepack"] = "python"
|
||||
data["trigger_actions"] = ["test", "build", "deploy"]
|
||||
data["ports"] = [80]
|
||||
|
||||
source = {}
|
||||
source['repository'] = "https://github.com"
|
||||
source['revision'] = "master"
|
||||
data["source"] = source
|
||||
|
||||
workflow = {}
|
||||
workflow["test_cmd"] = "./unit_tests.sh"
|
||||
workflow["run_cmd"] = "python app.py"
|
||||
data["workflow_config"] = workflow
|
||||
|
||||
return data
|
||||
|
||||
def _assert_app_data(self, actual, expected):
|
||||
self.assertEqual(actual["name"], expected["name"])
|
||||
self.assertEqual(actual["description"], expected["description"])
|
||||
|
@ -71,13 +52,13 @@ class TestAppController(base.TestCase):
|
|||
self.client.delete_created_apps()
|
||||
|
||||
def test_app_create(self):
|
||||
data = self._get_sample_data()
|
||||
data = apputils.get_sample_data()
|
||||
resp = self.client.create_app(data=data)
|
||||
self.assertEqual(resp.status, 201)
|
||||
|
||||
def test_app_create_bad_port_data(self):
|
||||
try:
|
||||
bad_data = self._get_sample_data()
|
||||
bad_data = apputils.get_sample_data()
|
||||
bad_data["ports"][0] = -1
|
||||
self.client.create_plan(data=bad_data)
|
||||
except tempest_exceptions.BadRequest:
|
||||
|
@ -89,7 +70,7 @@ class TestAppController(base.TestCase):
|
|||
headers={'content-type': 'application/json'})
|
||||
|
||||
def test_app_patch(self):
|
||||
data = self._get_sample_data()
|
||||
data = apputils.get_sample_data()
|
||||
create_resp = self.client.create_app(data=data)
|
||||
self.assertEqual(create_resp.status, 201)
|
||||
|
||||
|
@ -116,7 +97,7 @@ class TestAppController(base.TestCase):
|
|||
self.assertEqual(app_body["source"]["repository"], "newrepo")
|
||||
|
||||
def test_app_get(self):
|
||||
data = self._get_sample_data()
|
||||
data = apputils.get_sample_data()
|
||||
create_resp = self.client.create_app(data=data)
|
||||
self.assertEqual(create_resp.status, 201)
|
||||
id = create_resp.id
|
||||
|
@ -129,7 +110,7 @@ class TestAppController(base.TestCase):
|
|||
self._assert_app_data(yaml_data, data)
|
||||
|
||||
def test_apps_get_all(self):
|
||||
data = self._get_sample_data()
|
||||
data = apputils.get_sample_data()
|
||||
create_resp = self.client.create_app(data)
|
||||
self.assertEqual(create_resp.status, 201)
|
||||
resp, body = self.client.get(
|
||||
|
@ -141,7 +122,7 @@ class TestAppController(base.TestCase):
|
|||
self.assertEqual(filtered[0]['id'], id)
|
||||
|
||||
def test_app_delete(self):
|
||||
data = self._get_sample_data()
|
||||
data = apputils.get_sample_data()
|
||||
create_resp = self.client.create_app(data)
|
||||
self.assertEqual(create_resp.status, 201)
|
||||
id = create_resp.id
|
||||
|
|
|
@ -106,7 +106,6 @@ class AppsController(rest.RestController):
|
|||
|
||||
new_app = handler.create(app_data)
|
||||
created_app = app.App.from_db_model(new_app, pecan.request.host_url)
|
||||
|
||||
return created_app
|
||||
|
||||
@exception.wrap_wsme_pecan_controller_exception
|
||||
|
|
|
@ -47,10 +47,12 @@ class App(api_types.Base):
|
|||
workflow_config = {wtypes.text: wtypes.text}
|
||||
trigger_uuid = wtypes.text
|
||||
trigger_actions = [wtypes.text]
|
||||
trigger_uri = wtypes.text
|
||||
trust_id = wtypes.text
|
||||
trust_user = wtypes.text
|
||||
app_url = wtypes.text
|
||||
status = wtypes.text
|
||||
repo_token = wtypes.text
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(App, self).__init__(*args, **kwargs)
|
||||
|
@ -67,6 +69,8 @@ class App(api_types.Base):
|
|||
json = m.as_dict()
|
||||
json['type'] = m.__tablename__
|
||||
json['uri'] = '%s/v1/apps/%s' % (host_url, m.id)
|
||||
json['trigger_uri'] = ('%s/v1/triggers/%s' %
|
||||
(host_url, m.trigger_uuid))
|
||||
return cls(**(json))
|
||||
|
||||
def as_dict(self, db_model):
|
||||
|
|
|
@ -18,8 +18,7 @@ from oslo_config import cfg
|
|||
import pecan
|
||||
from pecan import rest
|
||||
|
||||
from solum.api.handlers import pipeline_handler
|
||||
from solum.api.handlers import plan_handler
|
||||
from solum.api.handlers import app_handler
|
||||
from solum.common import exception
|
||||
from solum.openstack.common import log as logging
|
||||
|
||||
|
@ -47,6 +46,7 @@ class TriggerController(rest.RestController):
|
|||
status_url = None
|
||||
collab_url = None
|
||||
workflow = None
|
||||
|
||||
try:
|
||||
query = query_dict(pecan.request.query_string)
|
||||
if 'workflow' in query:
|
||||
|
@ -96,11 +96,11 @@ class TriggerController(rest.RestController):
|
|||
raise exception.BadRequest(reason=info_msg)
|
||||
|
||||
try:
|
||||
handler = plan_handler.PlanHandler(None)
|
||||
handler = app_handler.AppHandler(None)
|
||||
handler.trigger_workflow(trigger_id, commit_sha, status_url,
|
||||
collab_url, workflow=workflow)
|
||||
except exception.ResourceNotFound:
|
||||
handler = pipeline_handler.PipelineHandler(None)
|
||||
handler.trigger_workflow(trigger_id)
|
||||
except exception.ResourceNotFound as e:
|
||||
LOG.error("Incorrect trigger url.")
|
||||
raise e
|
||||
|
||||
pecan.response.status = 202
|
||||
|
|
|
@ -78,7 +78,9 @@ class WorkflowsController(rest.RestController):
|
|||
data.source = app_model.source
|
||||
|
||||
wf_data = data.as_dict(workflow.Workflow)
|
||||
return workflow.Workflow.from_db_model(handler.create(wf_data),
|
||||
return workflow.Workflow.from_db_model(handler.create(wf_data,
|
||||
commit_sha='',
|
||||
status_url=''),
|
||||
pecan.request.host_url)
|
||||
|
||||
@exception.wrap_pecan_controller_exception
|
||||
|
|
|
@ -15,6 +15,9 @@
|
|||
import uuid
|
||||
|
||||
from solum.api.handlers import handler
|
||||
from solum.api.handlers import workflow_handler
|
||||
from solum.common import exception
|
||||
from solum.common import keystone_utils
|
||||
from solum import objects
|
||||
from solum.openstack.common import log as logging
|
||||
|
||||
|
@ -62,6 +65,10 @@ class AppHandler(handler.Handler):
|
|||
db_obj.project_id = self.context.tenant
|
||||
db_obj.deleted = False
|
||||
|
||||
# create a delegation trust_id\token, if required
|
||||
db_obj.trust_id = keystone_utils.create_delegation_token(self.context)
|
||||
db_obj.trust_user = self.context.user_name
|
||||
|
||||
db_obj.name = data.get('name')
|
||||
db_obj.description = data.get('description')
|
||||
db_obj.languagepack = data.get('languagepack')
|
||||
|
@ -69,14 +76,53 @@ class AppHandler(handler.Handler):
|
|||
db_obj.ports = data.get('ports')
|
||||
db_obj.source = data.get('source')
|
||||
db_obj.workflow_config = data.get('workflow_config')
|
||||
db_obj.trigger_uuid = data.get('trigger_uuid')
|
||||
db_obj.trigger_uuid = str(uuid.uuid4())
|
||||
db_obj.trigger_actions = data.get('trigger_actions')
|
||||
db_obj.trust_id = data.get('trust_id')
|
||||
db_obj.trust_user = data.get('trust_user')
|
||||
|
||||
db_obj.create(self.context)
|
||||
return db_obj
|
||||
|
||||
def trigger_workflow(self, trigger_id, commit_sha='',
|
||||
status_url=None, collab_url=None, workflow=None):
|
||||
"""Get trigger by trigger id and start git workflow associated."""
|
||||
# Note: self.context will be None at this point as this is a
|
||||
# non-authenticated request.
|
||||
app_obj = objects.registry.App.get_by_trigger_id(None, trigger_id)
|
||||
# get the trust context and authenticate it.
|
||||
try:
|
||||
self.context = keystone_utils.create_delegation_context(
|
||||
app_obj, self.context)
|
||||
self.context.tenant = app_obj.project_id
|
||||
self.context.user = app_obj.user_id
|
||||
self.context.user_name = app_obj.trust_user
|
||||
|
||||
except exception.AuthorizationFailure as auth_ex:
|
||||
LOG.warn(auth_ex)
|
||||
return
|
||||
|
||||
# TODO(devkulkarni): Call repo_utils.verify_artifact
|
||||
# as we are calling it in the plan_handler to verify
|
||||
# the collaborator
|
||||
self._build_artifact(app_obj, commit_sha=commit_sha,
|
||||
status_url=status_url, wf=workflow)
|
||||
|
||||
def _build_artifact(self, app, commit_sha='',
|
||||
status_url=None, wf=None):
|
||||
|
||||
if wf is None:
|
||||
wf = ['unittest', 'build', 'deploy']
|
||||
wfhand = workflow_handler.WorkflowHandler(self.context)
|
||||
|
||||
wfdata = {
|
||||
'app_id': app.id,
|
||||
'name': "%s" % app.name,
|
||||
'description': '',
|
||||
'source': app.source,
|
||||
'config': app.workflow_config,
|
||||
'actions': wf
|
||||
}
|
||||
wfhand.create(wfdata, commit_sha=commit_sha, status_url=status_url)
|
||||
|
||||
def get_all(self):
|
||||
"""Return all apps."""
|
||||
all_apps = objects.registry.AppList.get_all(self.context)
|
||||
|
|
|
@ -64,7 +64,7 @@ class WorkflowHandler(handler.Handler):
|
|||
db_obj = objects.registry.Workflow.get_by_uuid(self.context, id)
|
||||
db_obj.destroy(self.context)
|
||||
|
||||
def create(self, data):
|
||||
def create(self, data, commit_sha, status_url):
|
||||
"""Create a new workflow."""
|
||||
db_obj = objects.registry.Workflow()
|
||||
db_obj.id = str(uuid.uuid4())
|
||||
|
|
|
@ -18,19 +18,16 @@ import mock
|
|||
from oslo_config import cfg
|
||||
|
||||
from solum.api.controllers.v1 import trigger
|
||||
from solum.common import exception
|
||||
from solum.tests import base
|
||||
from solum.tests import fakes
|
||||
|
||||
|
||||
@mock.patch('pecan.request', new_callable=fakes.FakePecanRequest)
|
||||
@mock.patch('pecan.response', new_callable=fakes.FakePecanResponse)
|
||||
@mock.patch('solum.api.controllers.v1.trigger.plan_handler'
|
||||
'.PlanHandler')
|
||||
@mock.patch('solum.api.controllers.v1.trigger.pipeline_handler'
|
||||
'.PipelineHandler')
|
||||
@mock.patch('solum.api.controllers.v1.trigger.app_handler'
|
||||
'.AppHandler')
|
||||
class TestTriggerController(base.BaseTestCase):
|
||||
def test_trigger_post_with_empty_body(self, pipe_mock, assem_mock,
|
||||
def test_trigger_post_with_empty_body(self, assem_mock,
|
||||
resp_mock, request_mock):
|
||||
obj = trigger.TriggerController()
|
||||
obj.post('test_id')
|
||||
|
@ -38,7 +35,7 @@ class TestTriggerController(base.BaseTestCase):
|
|||
tw = assem_mock.return_value.trigger_workflow
|
||||
assert not tw.called
|
||||
|
||||
def test_trigger_post_on_github_webhook(self, pipe_mock, assem_mock,
|
||||
def test_trigger_post_on_github_webhook(self, assem_mock,
|
||||
resp_mock, request_mock):
|
||||
status_url = 'https://api.github.com/repos/u/r/statuses/{sha}'
|
||||
body_dict = {'sender': {'url': 'https://api.github.com'},
|
||||
|
@ -53,7 +50,7 @@ class TestTriggerController(base.BaseTestCase):
|
|||
tw.assert_called_once_with('test_id', 'asdf', expected_st_url, None,
|
||||
workflow=None)
|
||||
|
||||
def test_trigger_post_on_github_comment_webhook(self, pipe_mock,
|
||||
def test_trigger_post_on_github_comment_webhook(self,
|
||||
assem_mock, resp_mock,
|
||||
request_mock):
|
||||
cfg.CONF.api.rebuild_phrase = "solum retry tests"
|
||||
|
@ -78,7 +75,6 @@ class TestTriggerController(base.BaseTestCase):
|
|||
|
||||
@mock.patch('httplib2.Http.request')
|
||||
def test_trigger_post_on_mismatch_comment_pub_repo(self, http_mock,
|
||||
pipe_mock,
|
||||
assem_mock, resp_mock,
|
||||
request_mock):
|
||||
cfg.CONF.api.rebuild_phrase = "solum retry tests"
|
||||
|
@ -102,7 +98,6 @@ class TestTriggerController(base.BaseTestCase):
|
|||
|
||||
@mock.patch('httplib2.Http.request')
|
||||
def test_trigger_post_on_valid_comment_pub_repo(self, http_mock,
|
||||
pipe_mock,
|
||||
assem_mock, resp_mock,
|
||||
request_mock):
|
||||
cfg.CONF.api.rebuild_phrase = "solum retry tests"
|
||||
|
@ -127,7 +122,7 @@ class TestTriggerController(base.BaseTestCase):
|
|||
tw.assert_called_once_with('test_id', 'asdf', expected_st_url,
|
||||
expected_clb_url, workflow=None)
|
||||
|
||||
def test_trigger_post_on_comment_missing_login(self, pipe_mock,
|
||||
def test_trigger_post_on_comment_missing_login(self,
|
||||
assem_mock, resp_mock,
|
||||
request_mock):
|
||||
cfg.CONF.api.rebuild_phrase = "solum retry tests"
|
||||
|
@ -148,7 +143,7 @@ class TestTriggerController(base.BaseTestCase):
|
|||
tw = assem_mock.return_value.trigger_workflow
|
||||
assert not tw.called
|
||||
|
||||
def test_trigger_post_on_wrong_github_webhook(self, pipe_mock, assem_mock,
|
||||
def test_trigger_post_on_wrong_github_webhook(self, assem_mock,
|
||||
resp_mock, request_mock):
|
||||
status_url = 'https://api.github.com/repos/u/r/statuses/{sha}'
|
||||
body_dict = {'sender': {'url': 'https://api.github.com'},
|
||||
|
@ -161,7 +156,7 @@ class TestTriggerController(base.BaseTestCase):
|
|||
tw = assem_mock.return_value.trigger_workflow
|
||||
assert not tw.called
|
||||
|
||||
def test_trigger_post_on_unknown_git_webhook(self, pipe_mock, assem_mock,
|
||||
def test_trigger_post_on_unknown_git_webhook(self, assem_mock,
|
||||
resp_mock, request_mock):
|
||||
body_dict = {"pull_request": {"head": {"sha": "asdf"}}}
|
||||
request_mock.body = json.dumps(body_dict)
|
||||
|
@ -171,7 +166,7 @@ class TestTriggerController(base.BaseTestCase):
|
|||
tw = assem_mock.return_value.trigger_workflow
|
||||
assert not tw.called
|
||||
|
||||
def test_trigger_post_on_non_github_webhook(self, pipe_mock, assem_mock,
|
||||
def test_trigger_post_on_non_github_webhook(self, assem_mock,
|
||||
resp_mock, request_mock):
|
||||
body_dict = {"sender": {"url": "https://non-github.com"},
|
||||
"pull_request": {"head": {"sha": "asdf"}}}
|
||||
|
@ -182,7 +177,7 @@ class TestTriggerController(base.BaseTestCase):
|
|||
tw = assem_mock.return_value.trigger_workflow
|
||||
assert not tw.called
|
||||
|
||||
def test_trigger_post_on_github_ping_webhook(self, pipe_mock, assem_mock,
|
||||
def test_trigger_post_on_github_ping_webhook(self, assem_mock,
|
||||
resp_mock, request_mock):
|
||||
body_dict = {"sender": {"url": "https://api.github.com"},
|
||||
"zen": "Keep it logically awesome."}
|
||||
|
@ -191,41 +186,4 @@ class TestTriggerController(base.BaseTestCase):
|
|||
obj.post('test_id')
|
||||
self.assertEqual(501, resp_mock.status)
|
||||
tw = assem_mock.return_value.trigger_workflow
|
||||
assert not tw.called
|
||||
|
||||
def test_trigger_post_pipeline(self, pipe_mock, assem_mock,
|
||||
resp_mock, request_mock):
|
||||
status_url = 'https://api.github.com/repos/u/r/statuses/{sha}'
|
||||
body_dict = {'sender': {'url': 'https://api.github.com'},
|
||||
'pull_request': {'head': {'sha': 'asdf'}},
|
||||
'repository': {'statuses_url': status_url}}
|
||||
request_mock.body = json.dumps(body_dict)
|
||||
obj = trigger.TriggerController()
|
||||
assem_mock.return_value.trigger_workflow.side_effect = (
|
||||
exception.ResourceNotFound(name='trigger', id='test_id'))
|
||||
obj.post('test_id')
|
||||
|
||||
self.assertEqual(202, resp_mock.status)
|
||||
tw = pipe_mock.return_value.trigger_workflow
|
||||
tw.assert_called_once_with('test_id')
|
||||
|
||||
def test_trigger_post_none(self, pipe_mock, assem_mock,
|
||||
resp_mock, request_mock):
|
||||
status_url = 'https://api.github.com/repos/u/r/statuses/{sha}'
|
||||
body_dict = {'sender': {'url': 'https://api.github.com'},
|
||||
'pull_request': {'head': {'sha': 'asdf'}},
|
||||
'repository': {'statuses_url': status_url}}
|
||||
request_mock.body = json.dumps(body_dict)
|
||||
expected_st_url = 'https://api.github.com/repos/u/r/statuses/asdf'
|
||||
obj = trigger.TriggerController()
|
||||
assem_mock.return_value.trigger_workflow.side_effect = (
|
||||
exception.ResourceNotFound(name='trigger', id='test_id'))
|
||||
pipe_mock.return_value.trigger_workflow.side_effect = (
|
||||
exception.ResourceNotFound(name='trigger', id='test_id'))
|
||||
obj.post('test_id')
|
||||
self.assertEqual(404, resp_mock.status)
|
||||
tw = assem_mock.return_value.trigger_workflow
|
||||
tw.assert_called_once_with('test_id', 'asdf', expected_st_url, None,
|
||||
workflow=None)
|
||||
tw = pipe_mock.return_value.trigger_workflow
|
||||
tw.assert_called_once_with('test_id')
|
||||
assert not tw.called
|
|
@ -51,7 +51,8 @@ class TestAppHandler(base.BaseTestCase):
|
|||
get_by_uuid.assert_called_once_with(self.ctx, 'test_id')
|
||||
self.assertTrue(db_obj.destroy.called)
|
||||
|
||||
def test_app_create(self, mock_registry):
|
||||
@mock.patch('solum.common.clients.OpenStackClients.keystone')
|
||||
def test_app_create(self, mock_kc, mock_registry):
|
||||
data = {'name': 'fakeapp',
|
||||
'description': 'fake app for testing'}
|
||||
db_obj = fakes.FakeApp()
|
||||
|
|
|
@ -91,8 +91,7 @@ class TestWorkflowHandler(base.BaseTestCase):
|
|||
|
||||
handler = workflow_handler.WorkflowHandler(self.ctx)
|
||||
|
||||
res = handler.create(workflow_data)
|
||||
|
||||
res = handler.create(workflow_data, commit_sha='', status_url='')
|
||||
self.assertEqual(wf_obj, res)
|
||||
git_info = {
|
||||
'source_url': app_obj.source['repository'],
|
||||
|
|
|
@ -46,14 +46,14 @@ def _get_solum_client():
|
|||
|
||||
def main(args):
|
||||
client = _get_solum_client()
|
||||
plan = client.plans.find(name_or_id=args.app)
|
||||
app = client.apps.find(name_or_id=args.app)
|
||||
status_url = 'https://api.github.com/repos/{repo}/statuses/{sha}'.format(
|
||||
repo=args.repo, sha=args.sha)
|
||||
body_dict = {'sender': {'url': 'https://api.github.com'},
|
||||
'pull_request': {'head': {'sha': args.sha}},
|
||||
'repository': {'statuses_url': status_url}}
|
||||
body = json.dumps(body_dict)
|
||||
trigger_uri = plan.trigger_uri
|
||||
trigger_uri = app.trigger_uri
|
||||
if args.workflow:
|
||||
trigger_uri = "%s?workflow=%s" % (trigger_uri, args.workflow)
|
||||
resp = requests.post(trigger_uri, data=body)
|
||||
|
|
Loading…
Reference in New Issue