diff --git a/doc/pagure_ci.rst b/doc/pagure_ci.rst index ec973cc..6f76c31 100644 --- a/doc/pagure_ci.rst +++ b/doc/pagure_ci.rst @@ -22,11 +22,13 @@ How to enable Pagure CI All of which are required field. -* The jenkins token is any string that you give here. The only thing that should be - kept in mind that this token should be same through out. +* The jenkins token is any string that you give here. The only thing that should + be kept in mind that this token should be same through out. * This will give a POST URL which will be used for Job Notification in Jenkins +* The POST url will only appear only after you successfully submitted the form. + Configuring Jenkins =================== @@ -37,6 +39,10 @@ after you login to your Jenkins Instance. * Go to Manage Jenkins -> Configuire Global Security and under that select `Project-based Matrix Authorization Strategy` +* Add your username here and make sure to give that username all the permissions. + You should give all the permissions possible so that you save your self from + getting locked in Jenkins. + * Download the following plugins: :: @@ -61,7 +67,7 @@ after you login to your Jenkins Instance. FORMAT: JSON PROTOCOL: HTTP - EVENT: All Event + EVENT: Job Finalized URL: TIMEOUT: 3000 LOG: 1 @@ -72,7 +78,7 @@ after you login to your Jenkins Instance. * Two string parameters need to be created REPO and BRANCH -* Source Code Management select Git and give the URL of the pagure project +* Source Code Management select Git and give the URL of the pagure project * Under Build Trigger click on Trigger build remotely and give the same token that you gave in the Pagure CI form. diff --git a/pagure/hooks/jenkins_hook.py b/pagure/hooks/jenkins_hook.py index 1918c4a..59088b0 100644 --- a/pagure/hooks/jenkins_hook.py +++ b/pagure/hooks/jenkins_hook.py @@ -2,9 +2,11 @@ import os +import uuid import sqlalchemy as sa import pygit2 + from wtforms import validators, TextField, BooleanField from flask.ext import wtf from sqlalchemy.orm import relation, backref @@ -27,11 +29,13 @@ class PagureCI(BASE): nullable=False, unique=False, index=True) + pagure_ci_token = sa.Column(sa.String(64), nullable=True, unique=True, + index=True) active = sa.Column(sa.Boolean, nullable=False, default=False) display_name = sa.Column(sa.String(64), nullable=False, default='Jenkins') pagure_name = sa.Column(sa.String(255)) - + jenkins_name = sa.Column(sa.String(255)) jenkins_url = sa.Column(sa.String(255), nullable=False, default='http://jenkins.fedorainfracloud.org/') @@ -46,6 +50,10 @@ class PagureCI(BASE): single_parent=True) ) + def __init__(self): + self.pagure_ci_token = uuid.uuid4().hex + + class ConfigNotFound(Exception): pass diff --git a/pagure/lib/pagure_ci.py b/pagure/lib/pagure_ci.py index a4aa7c2..8d29540 100644 --- a/pagure/lib/pagure_ci.py +++ b/pagure/lib/pagure_ci.py @@ -23,48 +23,58 @@ PAGURE_URL = '{base}api/0/{repo}/pull-request/{pr}/flag' JENKINS_TRIGGER_URL = '{base}job/{project}/buildWithParameters' +class HookInactive(Exception): + pass + + def process_pr(logger, cfg, pr_id, repo, branch): - post_data(logger, - JENKINS_TRIGGER_URL.format( - base=cfg.jenkins_url, project=cfg.jenkins_name), - {'token': cfg.jenkins_token, - 'cause': pr_id, - 'REPO': repo, - 'BRANCH': branch}) + if cfg.active: + post_data(logger, + JENKINS_TRIGGER_URL.format( + base=cfg.jenkins_url, project=cfg.jenkins_name), + {'token': cfg.jenkins_token, + 'cause': pr_id, + 'REPO': repo, + 'BRANCH': branch}) + else: + raise HookInactive(cfg.pagure_name) def process_build(logger, cfg, build_id): - # Get details from Jenkins - jenk = jenkins.Jenkins(cfg.jenkins_url) - build_info = jenk.get_build_info(cfg.jenkins_name, build_id) - result = build_info['result'] - url = build_info['url'] - - pr_id = None - - for action in build_info['actions']: - for cause in action.get('causes', []): - try: - pr_id = int(cause['note']) - except (KeyError, ValueError): - continue - - if not pr_id: - logger.info('Not a PR check') - return - - # Comment in Pagure - logger.info('Updating %s PR %d: %s', cfg.pagure_name, pr_id, result) - try: - pagure_ci_flag(logger, - username=cfg.display_name, - repo=cfg.pagure_name, - requestid=pr_id, - result=result, - url=url) - - except KeyError as exc: - logger.warning('Unknown build status', exc_info=exc) + if cfg.active: + # Get details from Jenkins + jenk = jenkins.Jenkins(cfg.jenkins_url) + build_info = jenk.get_build_info(cfg.jenkins_name, build_id) + result = build_info['result'] + url = build_info['url'] + + pr_id = None + + for action in build_info['actions']: + for cause in action.get('causes', []): + try: + pr_id = int(cause['note']) + except (KeyError, ValueError): + continue + + if not pr_id: + logger.info('Not a PR check') + return + + # Comment in Pagure + logger.info('Updating %s PR %d: %s', cfg.pagure_name, pr_id, result) + try: + pagure_ci_flag(logger, + username=cfg.display_name, + repo=cfg.pagure_name, + requestid=pr_id, + result=result, + url=url) + + except KeyError as exc: + logger.warning('Unknown build status', exc_info=exc) + else: + raise HookInactive(cfg.pagure_name) def post_data(logger, *args, **kwargs): diff --git a/pagure/templates/plugin.html b/pagure/templates/plugin.html index bf10f6a..d98af1a 100644 --- a/pagure/templates/plugin.html +++ b/pagure/templates/plugin.html @@ -17,11 +17,11 @@ ) }}" method="post"> {{ plugin.description | markdown | noJS | safe }} - {% if plugin.name == 'Pagure CI' %} + {% if pagure_ci_token and (plugin.name == 'Pagure CI') %}
- +
{% endif %} diff --git a/pagure/ui/plugins.py b/pagure/ui/plugins.py index d1dd5ae..06a5d5b 100644 --- a/pagure/ui/plugins.py +++ b/pagure/ui/plugins.py @@ -90,6 +90,7 @@ def view_plugin(repo, plugin, username=None, full=True): fields = [] new = True dbobj = plugin.db_object() + pagure_ci_token = None if hasattr(repo, plugin.backref): dbobj = getattr(repo, plugin.backref) @@ -98,6 +99,9 @@ def view_plugin(repo, plugin, username=None, full=True): if dbobj and len(dbobj) > 0: dbobj = dbobj[0] new = False + # To populate the pagure CI token if generated + if hasattr(dbobj, "pagure_ci_token") and plugin.backref == "hook_pagure_ci": + pagure_ci_token = dbobj.pagure_ci_token else: dbobj = plugin.db_object() @@ -129,6 +133,7 @@ def view_plugin(repo, plugin, username=None, full=True): username=username, plugin=plugin, form=form, + pagure_ci_token=pagure_ci_token, fields=fields) if form.active.data: @@ -162,21 +167,22 @@ def view_plugin(repo, plugin, username=None, full=True): username=username, plugin=plugin, form=form, + pagure_ci_token=pagure_ci_token, fields=fields) -@APP.route('/hooks//build-finished', methods=['POST']) -def hook_finished(repo_id): +@APP.route('/hooks//build-finished', methods=['POST']) +def hook_finished(pagure_ci_token): try: data = json.loads(flask.request.get_data()) cfg = jenkins_hook.get_configs( data['name'], jenkins_hook.Service.JENKINS)[0] build_id = data['build']['number'] - if repo_id != str(cfg.project_id): - raise ValueError('Project ID mismatch') + if pagure_ci_token != cfg.pagure_ci_token: + raise ValueError('Token mismatch') except (TypeError, ValueError, KeyError, jenkins_hook.ConfigNotFound) as exc: APP.logger.error('Error processing jenkins notification', exc_info=exc) - return ('Bad request...\n', 400, {'Content-Type': 'text/plain'}) + flask.abort(400, "Bad Request") APP.logger.info('Received jenkins notification') pagure_ci.process_build(APP.logger, cfg, build_id) return ('', 204) diff --git a/pagureCI/consumer.py b/pagureCI/consumer.py index 9960416..450f385 100644 --- a/pagureCI/consumer.py +++ b/pagureCI/consumer.py @@ -38,6 +38,8 @@ class Integrator(fedmsg.consumers.FedmsgConsumer): self.process_build(msg) except jenkins_hook.ConfigNotFound as exc: self.log.info('Unconfigured project %r', str(exc)) + except pagure_ci.HookInactive as exc: + self.log.info('Hook Inactive for project %r', str(exc)) def trigger_build(self, msg): pr_id = msg['pullrequest']['id']