Port triggers to new API.
This commit is contained in:
parent
e475e9809d
commit
21d0ec2012
4 changed files with 302 additions and 1 deletions
|
@ -179,3 +179,4 @@ import endpoints.api.user
|
|||
import endpoints.api.search
|
||||
import endpoints.api.build
|
||||
import endpoints.api.webhook
|
||||
import endpoints.api.trigger
|
||||
|
|
|
@ -1360,6 +1360,7 @@ def delete_webhook(namespace, repository, public_id):
|
|||
abort(403) # Permission denied
|
||||
|
||||
|
||||
# Ported
|
||||
@api_bp.route('/repository/<path:repository>/trigger/<trigger_uuid>',
|
||||
methods=['GET'])
|
||||
@api_login_required
|
||||
|
@ -1382,6 +1383,7 @@ def _prepare_webhook_url(scheme, username, password, hostname, path):
|
|||
return urlparse.urlunparse((scheme, auth_hostname, path, '', '', ''))
|
||||
|
||||
|
||||
# Ported
|
||||
@api_bp.route('/repository/<path:repository>/trigger/<trigger_uuid>/subdir',
|
||||
methods=['POST'])
|
||||
@api_login_required
|
||||
|
@ -1415,6 +1417,7 @@ def list_build_trigger_subdirs(namespace, repository, trigger_uuid):
|
|||
abort(403) # Permission denied
|
||||
|
||||
|
||||
# Ported
|
||||
@api_bp.route('/repository/<path:repository>/trigger/<trigger_uuid>/activate',
|
||||
methods=['POST'])
|
||||
@api_login_required
|
||||
|
@ -1474,6 +1477,7 @@ def activate_build_trigger(namespace, repository, trigger_uuid):
|
|||
abort(403) # Permission denied
|
||||
|
||||
|
||||
# Ported
|
||||
@api_bp.route('/repository/<path:repository>/trigger/<trigger_uuid>/start',
|
||||
methods=['POST'])
|
||||
@api_login_required
|
||||
|
@ -1512,6 +1516,7 @@ def manually_start_build_trigger(namespace, repository, trigger_uuid):
|
|||
abort(403) # Permission denied
|
||||
|
||||
|
||||
# Ported
|
||||
@api_bp.route('/repository/<path:repository>/trigger/<trigger_uuid>/builds',
|
||||
methods=['GET'])
|
||||
@api_login_required
|
||||
|
@ -1529,6 +1534,7 @@ def list_trigger_recent_builds(namespace, repository, trigger_uuid):
|
|||
abort(403) # Permission denied
|
||||
|
||||
|
||||
# Ported
|
||||
@api_bp.route('/repository/<path:repository>/trigger/<trigger_uuid>/sources',
|
||||
methods=['GET'])
|
||||
@api_login_required
|
||||
|
@ -1553,6 +1559,7 @@ def list_trigger_build_sources(namespace, repository, trigger_uuid):
|
|||
|
||||
|
||||
|
||||
# Ported
|
||||
@api_bp.route('/repository/<path:repository>/trigger/', methods=['GET'])
|
||||
@api_login_required
|
||||
@parse_repository_name
|
||||
|
@ -1567,6 +1574,7 @@ def list_build_triggers(namespace, repository):
|
|||
abort(403) # Permission denied
|
||||
|
||||
|
||||
# Ported
|
||||
@api_bp.route('/repository/<path:repository>/trigger/<trigger_uuid>',
|
||||
methods=['DELETE'])
|
||||
@api_login_required
|
||||
|
|
283
endpoints/api/trigger.py
Normal file
283
endpoints/api/trigger.py
Normal file
|
@ -0,0 +1,283 @@
|
|||
import json
|
||||
import logging
|
||||
|
||||
from flask import request, url_for
|
||||
from flask.ext.restful import abort
|
||||
from urllib import quote
|
||||
from urlparse import urlunparse
|
||||
|
||||
from app import app
|
||||
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
|
||||
log_action, request_error, query_param, parse_args,
|
||||
validate_json_request)
|
||||
from endpoints.api.build import build_status_view
|
||||
from endpoints.common import start_build
|
||||
from endpoints.trigger import (BuildTrigger, TriggerDeactivationException,
|
||||
TriggerActivationException, EmptyRepositoryException)
|
||||
from data import model
|
||||
from auth.permissions import UserPermission
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def trigger_view(trigger):
|
||||
if trigger and trigger.uuid:
|
||||
config_dict = json.loads(trigger.config)
|
||||
build_trigger = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
return {
|
||||
'service': trigger.service.name,
|
||||
'config': config_dict,
|
||||
'id': trigger.uuid,
|
||||
'connected_user': trigger.connected_user.username,
|
||||
'is_active': build_trigger.is_active(config_dict)
|
||||
}
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _prepare_webhook_url(scheme, username, password, hostname, path):
|
||||
auth_hostname = '%s:%s@%s' % (quote(username), quote(password), hostname)
|
||||
return urlunparse((scheme, auth_hostname, path, '', '', ''))
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/')
|
||||
class BuildTriggerList(RepositoryParamResource):
|
||||
""" Resource for listing repository build triggers. """
|
||||
|
||||
@require_repo_admin
|
||||
@nickname('listBuildTriggers')
|
||||
def get(self, namespace, repository):
|
||||
""" List the triggers for the specified repository. """
|
||||
triggers = model.list_build_triggers(namespace, repository)
|
||||
return {
|
||||
'triggers': [trigger_view(trigger) for trigger in triggers]
|
||||
}
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>')
|
||||
class BuildTrigger(RepositoryParamResource):
|
||||
""" Resource for managing specific build triggers. """
|
||||
|
||||
@require_repo_admin
|
||||
@nickname('getBuildTrigger')
|
||||
def get(self, namespace, repository, trigger_uuid):
|
||||
""" Get information for the specified build trigger. """
|
||||
try:
|
||||
trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
|
||||
except model.InvalidBuildTriggerException:
|
||||
abort(404)
|
||||
|
||||
return trigger_view(trigger)
|
||||
|
||||
@require_repo_admin
|
||||
@nickname('deleteBuildTrigger')
|
||||
def delete(self, namespace, repository, trigger_uuid):
|
||||
""" Delete the specified build trigger. """
|
||||
try:
|
||||
trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
|
||||
except model.InvalidBuildTriggerException:
|
||||
abort(404)
|
||||
return
|
||||
|
||||
handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
config_dict = json.loads(trigger.config)
|
||||
if handler.is_active(config_dict):
|
||||
try:
|
||||
handler.deactivate(trigger.auth_token, config_dict)
|
||||
except TriggerDeactivationException as ex:
|
||||
# We are just going to eat this error
|
||||
logger.warning('Trigger deactivation problem: %s', ex)
|
||||
|
||||
log_action('delete_repo_trigger', namespace,
|
||||
{'repo': repository, 'trigger_id': trigger_uuid,
|
||||
'service': trigger.service.name, 'config': config_dict},
|
||||
repo=model.get_repository(namespace, repository))
|
||||
|
||||
trigger.delete_instance(recursive=True)
|
||||
return 'No Content', 204
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/subdir')
|
||||
class BuildTriggerSubdirs(RepositoryParamResource):
|
||||
""" Custom verb for fetching the subdirs which are buildable for a trigger. """
|
||||
schemas = {
|
||||
'BuildTriggerSubdirRequest': {
|
||||
'id': 'BuildTriggerSubdirRequest',
|
||||
'type': 'object',
|
||||
'description': 'Arbitrary json.',
|
||||
'required': True,
|
||||
},
|
||||
}
|
||||
|
||||
@require_repo_admin
|
||||
@nickname('listBuildTriggerSubdirs')
|
||||
@validate_json_request('BuildTriggerSubdirRequest')
|
||||
def post(self, namespace, repository, trigger_uuid):
|
||||
""" List the subdirectories available for the specified build trigger and source. """
|
||||
try:
|
||||
trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
|
||||
except model.InvalidBuildTriggerException:
|
||||
abort(404)
|
||||
return
|
||||
|
||||
handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
user_permission = UserPermission(trigger.connected_user.username)
|
||||
if user_permission.can():
|
||||
new_config_dict = request.get_json()
|
||||
|
||||
try:
|
||||
subdirs = handler.list_build_subdirs(trigger.auth_token, new_config_dict)
|
||||
return {
|
||||
'subdir': subdirs,
|
||||
'status': 'success'
|
||||
}
|
||||
except EmptyRepositoryException as exc:
|
||||
return {
|
||||
'status': 'error',
|
||||
'message': exc.msg
|
||||
}
|
||||
else:
|
||||
abort(403)
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/activate')
|
||||
class BuildTriggerActivate(RepositoryParamResource):
|
||||
""" Custom verb for activating a build trigger once all required information has been collected.
|
||||
"""
|
||||
schemas = {
|
||||
'BuildTriggerActivateRequest': {
|
||||
'id': 'BuildTriggerActivateRequest',
|
||||
'type': 'object',
|
||||
'description': 'Arbitrary json.',
|
||||
'required': True,
|
||||
},
|
||||
}
|
||||
|
||||
@require_repo_admin
|
||||
@nickname('activateBuildTrigger')
|
||||
@validate_json_request('BuildTriggerActivateRequest')
|
||||
def post(self, namespace, repository, trigger_uuid):
|
||||
""" Activate the specified build trigger. """
|
||||
try:
|
||||
trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
|
||||
except model.InvalidBuildTriggerException:
|
||||
abort(404)
|
||||
|
||||
handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
existing_config_dict = json.loads(trigger.config)
|
||||
if handler.is_active(existing_config_dict):
|
||||
abort(400)
|
||||
|
||||
user_permission = UserPermission(trigger.connected_user.username)
|
||||
if user_permission.can():
|
||||
new_config_dict = request.get_json()
|
||||
|
||||
token_name = 'Build Trigger: %s' % trigger.service.name
|
||||
token = model.create_delegate_token(namespace, repository, token_name,
|
||||
'write')
|
||||
|
||||
try:
|
||||
repository_path = '%s/%s' % (trigger.repository.namespace,
|
||||
trigger.repository.name)
|
||||
path = url_for('webhooks.build_trigger_webhook',
|
||||
repository=repository_path, trigger_uuid=trigger.uuid)
|
||||
authed_url = _prepare_webhook_url(app.config['URL_SCHEME'], '$token',
|
||||
token.code, app.config['URL_HOST'],
|
||||
path)
|
||||
|
||||
final_config = handler.activate(trigger.uuid, authed_url,
|
||||
trigger.auth_token, new_config_dict)
|
||||
except TriggerActivationException as exc:
|
||||
token.delete_instance()
|
||||
return request_error(message=exc.message)
|
||||
|
||||
# Save the updated config.
|
||||
trigger.config = json.dumps(final_config)
|
||||
trigger.write_token = token
|
||||
trigger.save()
|
||||
|
||||
# Log the trigger setup.
|
||||
repo = model.get_repository(namespace, repository)
|
||||
log_action('setup_repo_trigger', namespace,
|
||||
{'repo': repository, 'namespace': namespace,
|
||||
'trigger_id': trigger.uuid, 'service': trigger.service.name,
|
||||
'config': final_config}, repo=repo)
|
||||
|
||||
return trigger_view(trigger)
|
||||
else:
|
||||
abort(403)
|
||||
|
||||
|
||||
@resource('/repository/<path:repository>/trigger/<trigger_uuid>/start')
|
||||
class ActivateBuildTrigger(RepositoryParamResource):
|
||||
""" Custom verb to manually activate a build trigger. """
|
||||
|
||||
@require_repo_admin
|
||||
@nickname('manuallyStartBuildTrigger')
|
||||
def post(self, namespace, repository, trigger_uuid):
|
||||
""" Manually start a build from the specified trigger. """
|
||||
try:
|
||||
trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
|
||||
except model.InvalidBuildTriggerException:
|
||||
abort(404)
|
||||
|
||||
handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
existing_config_dict = json.loads(trigger.config)
|
||||
if not handler.is_active(existing_config_dict):
|
||||
abort(400)
|
||||
|
||||
specs = handler.manual_start(trigger.auth_token, json.loads(trigger.config))
|
||||
dockerfile_id, tags, name, subdir = specs
|
||||
|
||||
repo = model.get_repository(namespace, repository)
|
||||
|
||||
build_request = start_build(repo, dockerfile_id, tags, name, subdir, True)
|
||||
|
||||
resp = build_status_view(build_request, True)
|
||||
repo_string = '%s/%s' % (namespace, repository)
|
||||
headers = {
|
||||
'Location': url_for('api_bp.get_repo_build_status', repository=repo_string,
|
||||
build_uuid=build_request.uuid),
|
||||
}
|
||||
return resp, 201, headers
|
||||
|
||||
|
||||
@resource('/v1/repository/<path:repository>/trigger/<trigger_uuid>/builds')
|
||||
class TriggerBuildList(RepositoryParamResource):
|
||||
""" Resource to represent builds that were activated from the specified trigger. """
|
||||
@parse_args
|
||||
@query_param('limit', 'The maximum number of builds to return', type=int, default=5)
|
||||
@require_repo_admin
|
||||
@nickname('listTriggerRecentBuilds')
|
||||
def get(self, args, namespace, repository, trigger_uuid):
|
||||
""" List the builds started by the specified trigger. """
|
||||
limit = args['limit']
|
||||
builds = list(model.list_trigger_builds(namespace, repository,
|
||||
trigger_uuid, limit))
|
||||
return {
|
||||
'builds': [build_status_view(build, True) for build in builds]
|
||||
}
|
||||
|
||||
|
||||
@resource('/repository/<path:repository>/trigger/<trigger_uuid>/sources')
|
||||
class BuildTriggerSources(RepositoryParamResource):
|
||||
""" Custom verb to fetch the list of build sources for the trigger config. """
|
||||
@require_repo_admin
|
||||
@nickname('listTriggerBuildSources')
|
||||
def get(self, namespace, repository, trigger_uuid):
|
||||
""" List the build sources for the trigger configuration thus far. """
|
||||
try:
|
||||
trigger = model.get_build_trigger(namespace, repository, trigger_uuid)
|
||||
except model.InvalidBuildTriggerException:
|
||||
abort(404)
|
||||
|
||||
user_permission = UserPermission(trigger.connected_user.username)
|
||||
if user_permission.can():
|
||||
trigger_handler = BuildTrigger.get_trigger_for_service(trigger.service.name)
|
||||
|
||||
return {
|
||||
'sources': trigger_handler.list_build_sources(trigger.auth_token)
|
||||
}
|
||||
else:
|
||||
abort(403)
|
|
@ -2,7 +2,7 @@ from flask import request, url_for
|
|||
from flask.ext.restful import abort
|
||||
|
||||
from endpoints.api import (RepositoryParamResource, nickname, resource, require_repo_admin,
|
||||
log_action)
|
||||
log_action, validate_json_request)
|
||||
from data import model
|
||||
|
||||
|
||||
|
@ -16,9 +16,18 @@ def webhook_view(webhook):
|
|||
@resource('/v1/repository/<path:repository>/webhook/')
|
||||
class WebhookList(RepositoryParamResource):
|
||||
""" Resource for dealing with listing and creating webhooks. """
|
||||
schemas = {
|
||||
'WebhookCreateRequest': {
|
||||
'id': 'WebhookCreateRequest',
|
||||
'type': 'object',
|
||||
'description': 'Arbitrary json.',
|
||||
'required': True,
|
||||
},
|
||||
}
|
||||
|
||||
@require_repo_admin
|
||||
@nickname('createWebhook')
|
||||
@validate_json_request('WebhookCreateRequest')
|
||||
def post(self, namespace, repository):
|
||||
""" Create a new webhook for the specified repository. """
|
||||
repo = model.get_repository(namespace, repository)
|
||||
|
|
Reference in a new issue