Github webhook handling
Closes-Bug: #1591847 Change-Id: I2ae11f441ed277635a5f0f711af8715dea0fc1b4
This commit is contained in:
parent
8db0c0d128
commit
61d28bc9ff
@ -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.
|
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.
|
16
examples/apps/github-reporting.yaml
Normal file
16
examples/apps/github-reporting.yaml
Normal 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
|
@ -63,6 +63,7 @@ class App(api_types.Base):
|
|||||||
created_at = datetime.datetime
|
created_at = datetime.datetime
|
||||||
updated_at = datetime.datetime
|
updated_at = datetime.datetime
|
||||||
raw_content = wtypes.text
|
raw_content = wtypes.text
|
||||||
|
repo_token = wtypes.text
|
||||||
scale_config = wtypes.DictType(
|
scale_config = wtypes.DictType(
|
||||||
wtypes.text,
|
wtypes.text,
|
||||||
wtypes.DictType(wtypes.text, wtypes.text))
|
wtypes.DictType(wtypes.text, wtypes.text))
|
||||||
@ -110,7 +111,8 @@ class App(api_types.Base):
|
|||||||
'trigger_actions',
|
'trigger_actions',
|
||||||
'workflow_config',
|
'workflow_config',
|
||||||
'stack_id',
|
'stack_id',
|
||||||
'raw_content'
|
'raw_content',
|
||||||
|
'repo_token'
|
||||||
]
|
]
|
||||||
base = super(App, self).as_dict(db_model)
|
base = super(App, self).as_dict(db_model)
|
||||||
|
|
||||||
|
@ -113,7 +113,8 @@ class TriggerController(rest.RestController):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Trigger workflow only on PR create and on rebuild request
|
# 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 = app_handler.AppHandler(None)
|
||||||
handler.trigger_workflow(trigger_id, commit_sha, status_url,
|
handler.trigger_workflow(trigger_id, commit_sha, status_url,
|
||||||
collab_url, workflow=workflow)
|
collab_url, workflow=workflow)
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from solum.api.handlers import common
|
from solum.api.handlers import common
|
||||||
@ -50,6 +51,12 @@ class AppHandler(handler.Handler):
|
|||||||
new_wf.update(data_dict.get('workflow_config', {}))
|
new_wf.update(data_dict.get('workflow_config', {}))
|
||||||
data_dict['workflow_config'] = new_wf
|
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,
|
updated = objects.registry.App.update_and_save(self.context,
|
||||||
id, data_dict)
|
id, data_dict)
|
||||||
return updated
|
return updated
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -110,6 +111,7 @@ class WorkflowHandler(handler.Handler):
|
|||||||
|
|
||||||
def create(self, data, commit_sha, status_url, du_id):
|
def create(self, data, commit_sha, status_url, du_id):
|
||||||
"""Create a new workflow."""
|
"""Create a new workflow."""
|
||||||
|
|
||||||
db_obj = objects.registry.Workflow()
|
db_obj = objects.registry.Workflow()
|
||||||
db_obj.id = str(uuid.uuid4())
|
db_obj.id = str(uuid.uuid4())
|
||||||
db_obj.user_id = self.context.user
|
db_obj.user_id = self.context.user
|
||||||
@ -149,6 +151,7 @@ class WorkflowHandler(handler.Handler):
|
|||||||
|
|
||||||
self._execute_workflow_actions(db_obj, app_obj, assem,
|
self._execute_workflow_actions(db_obj, app_obj, assem,
|
||||||
commit_sha=commit_sha,
|
commit_sha=commit_sha,
|
||||||
|
status_url=status_url,
|
||||||
du_id=du_id)
|
du_id=du_id)
|
||||||
|
|
||||||
# TODO(devkulkarni): Update status of actions
|
# TODO(devkulkarni): Update status of actions
|
||||||
@ -183,8 +186,10 @@ class WorkflowHandler(handler.Handler):
|
|||||||
run_cmd = wf_obj.config['run_cmd']
|
run_cmd = wf_obj.config['run_cmd']
|
||||||
|
|
||||||
ports = app_obj.ports
|
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:
|
else:
|
||||||
repo_token = ''
|
repo_token = ''
|
||||||
|
|
||||||
|
@ -94,7 +94,8 @@ class TestAppHandler(base.BaseTestCase):
|
|||||||
db_obj = objects.registry.App()
|
db_obj = objects.registry.App()
|
||||||
# Without this, I'd just get a mocked function call.
|
# Without this, I'd just get a mocked function call.
|
||||||
# I want real data so I can track that it's being updated.
|
# 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
|
mock_registry.App.get_by_uuid.return_value = db_obj
|
||||||
# I'm not saving anything anyway, I just want to make sure
|
# I'm not saving anything anyway, I just want to make sure
|
||||||
# that I'm sending the right data to update_and_save.
|
# that I'm sending the right data to update_and_save.
|
||||||
@ -110,6 +111,7 @@ class TestAppHandler(base.BaseTestCase):
|
|||||||
'source': {
|
'source': {
|
||||||
'revision': 'experimental',
|
'revision': 'experimental',
|
||||||
},
|
},
|
||||||
|
'repo_token': 'abc'
|
||||||
}
|
}
|
||||||
handler = app_handler.AppHandler(self.ctx)
|
handler = app_handler.AppHandler(self.ctx)
|
||||||
new_app = objects.registry.App()
|
new_app = objects.registry.App()
|
||||||
|
@ -100,7 +100,7 @@ class TestWorkflowHandler(base.BaseTestCase):
|
|||||||
'repo_token': app_obj.source['repo_token'],
|
'repo_token': app_obj.source['repo_token'],
|
||||||
'private': app_obj.source['private'],
|
'private': app_obj.source['private'],
|
||||||
'private_ssh_key': app_obj.source['private_ssh_key'],
|
'private_ssh_key': app_obj.source['private_ssh_key'],
|
||||||
'status_url': None,
|
'status_url': '',
|
||||||
}
|
}
|
||||||
mock_pa.assert_called_once_with(
|
mock_pa.assert_called_once_with(
|
||||||
verb='launch_workflow', workflow=['unittest', 'build', 'deploy'],
|
verb='launch_workflow', workflow=['unittest', 'build', 'deploy'],
|
||||||
|
@ -90,6 +90,7 @@ class FakeApp(mock.Mock):
|
|||||||
self.stack_id = ''
|
self.stack_id = ''
|
||||||
self.trigger_actions = ["unittest", "build", "deploy"]
|
self.trigger_actions = ["unittest", "build", "deploy"]
|
||||||
self.scale_config = dict()
|
self.scale_config = dict()
|
||||||
|
self.raw_content = "{\"repo_token\": \"test-repo-token\"}"
|
||||||
|
|
||||||
def as_dict(self):
|
def as_dict(self):
|
||||||
return {
|
return {
|
||||||
@ -104,6 +105,7 @@ class FakeApp(mock.Mock):
|
|||||||
'ports': self.ports,
|
'ports': self.ports,
|
||||||
'workflow_config': self.workflow_config,
|
'workflow_config': self.workflow_config,
|
||||||
'stack_id': self.stack_id,
|
'stack_id': self.stack_id,
|
||||||
|
'raw_content': self.raw_content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user