Github webhook handling

Closes-Bug: #1591847

Change-Id: I2ae11f441ed277635a5f0f711af8715dea0fc1b4
This commit is contained in:
Devdatta Kulkarni 2016-06-06 23:44:40 -05:00
parent 8db0c0d128
commit 61d28bc9ff
9 changed files with 67 additions and 6 deletions

View File

@ -57,7 +57,33 @@ Once the app is READY, you can curl to app_url that is displayed in the output
of the 'app show' command.
4) Github trigger and reporting
Use the following app file: github-reporting.yaml
Steps:
1) Fork the repository used in above app file in your account. Modify the app file to use this forked repo
2) Generate a Github access token and add it to the app file as repo_token. Then create the app.
solum app create --app-file github-reporting.yaml --setup-trigger
3) CLI will generate Github webhook and add it to your repository.
4) In order for github webhook to trigger Solum running on your local vm, you will need to expose
the Solum API to outside world. A tool called ngrok will allow you to do that.
Download ngrok and run it on the host
./ngrok http 9777
5) Go to your github repository and modify webhook to include the "Forwarding" address which is output
from running the ngrok command above.
6) Create a Pull request for your repo.
7) You will notice that Solum receives the webook and starts executing unittest workflow stage.
You can run the app show command to check this:
solum app show cherrypy-github
8) You can also see that the pull request on Github is updated to show the status of executing tests.

View File

@ -0,0 +1,16 @@
version: 1
name: cherrypy-github
description: python web app
languagepack: python
source:
repository: https://github.com/devkulkarni/solum-python-sample-app.git
revision: master
workflow_config:
test_cmd: ./unit_tests.sh
run_cmd: python app.py
trigger_actions:
- unittest
- build
- deploy
ports:
- 80

View File

@ -63,6 +63,7 @@ class App(api_types.Base):
created_at = datetime.datetime
updated_at = datetime.datetime
raw_content = wtypes.text
repo_token = wtypes.text
scale_config = wtypes.DictType(
wtypes.text,
wtypes.DictType(wtypes.text, wtypes.text))
@ -110,7 +111,8 @@ class App(api_types.Base):
'trigger_actions',
'workflow_config',
'stack_id',
'raw_content'
'raw_content',
'repo_token'
]
base = super(App, self).as_dict(db_model)

View File

@ -113,7 +113,8 @@ class TriggerController(rest.RestController):
try:
# Trigger workflow only on PR create and on rebuild request
if action in ['created', 'opened', 'edited', 'reopened']:
if action in ['created', 'opened', 'edited', 'reopened',
'synchronize', 'closed']:
handler = app_handler.AppHandler(None)
handler.trigger_workflow(trigger_id, commit_sha, status_url,
collab_url, workflow=workflow)

View File

@ -12,6 +12,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import json
import uuid
from solum.api.handlers import common
@ -50,6 +51,12 @@ class AppHandler(handler.Handler):
new_wf.update(data_dict.get('workflow_config', {}))
data_dict['workflow_config'] = new_wf
if data_dict['repo_token']:
new_raw_content = json.loads(obj_dict['raw_content'])
new_raw_content['repo_token'] = data_dict['repo_token']
data_dict['raw_content'] = json.dumps(new_raw_content)
del data_dict['repo_token']
updated = objects.registry.App.update_and_save(self.context,
id, data_dict)
return updated

View File

@ -13,6 +13,7 @@
# under the License.
import datetime
import json
import uuid
from oslo_config import cfg
@ -110,6 +111,7 @@ class WorkflowHandler(handler.Handler):
def create(self, data, commit_sha, status_url, du_id):
"""Create a new workflow."""
db_obj = objects.registry.Workflow()
db_obj.id = str(uuid.uuid4())
db_obj.user_id = self.context.user
@ -149,6 +151,7 @@ class WorkflowHandler(handler.Handler):
self._execute_workflow_actions(db_obj, app_obj, assem,
commit_sha=commit_sha,
status_url=status_url,
du_id=du_id)
# TODO(devkulkarni): Update status of actions
@ -183,8 +186,10 @@ class WorkflowHandler(handler.Handler):
run_cmd = wf_obj.config['run_cmd']
ports = app_obj.ports
if ('repo_token' in wf_obj.source.keys()):
repo_token = wf_obj.source['repo_token']
app_raw_content = json.loads(app_obj.raw_content)
if ('repo_token' in app_raw_content):
repo_token = app_raw_content['repo_token']
else:
repo_token = ''

View File

@ -94,7 +94,8 @@ class TestAppHandler(base.BaseTestCase):
db_obj = objects.registry.App()
# Without this, I'd just get a mocked function call.
# I want real data so I can track that it's being updated.
db_obj.as_dict.return_value = fakes.FakeApp().as_dict()
a = fakes.FakeApp().as_dict()
db_obj.as_dict.return_value = a
mock_registry.App.get_by_uuid.return_value = db_obj
# I'm not saving anything anyway, I just want to make sure
# that I'm sending the right data to update_and_save.
@ -110,6 +111,7 @@ class TestAppHandler(base.BaseTestCase):
'source': {
'revision': 'experimental',
},
'repo_token': 'abc'
}
handler = app_handler.AppHandler(self.ctx)
new_app = objects.registry.App()

View File

@ -100,7 +100,7 @@ class TestWorkflowHandler(base.BaseTestCase):
'repo_token': app_obj.source['repo_token'],
'private': app_obj.source['private'],
'private_ssh_key': app_obj.source['private_ssh_key'],
'status_url': None,
'status_url': '',
}
mock_pa.assert_called_once_with(
verb='launch_workflow', workflow=['unittest', 'build', 'deploy'],

View File

@ -90,6 +90,7 @@ class FakeApp(mock.Mock):
self.stack_id = ''
self.trigger_actions = ["unittest", "build", "deploy"]
self.scale_config = dict()
self.raw_content = "{\"repo_token\": \"test-repo-token\"}"
def as_dict(self):
return {
@ -104,6 +105,7 @@ class FakeApp(mock.Mock):
'ports': self.ports,
'workflow_config': self.workflow_config,
'stack_id': self.stack_id,
'raw_content': self.raw_content
}