Blob Blame Raw
# -*- coding: utf-8 -*-

"""
 (c) 2016-2017 - Copyright Red Hat Inc

 Authors:
   Pierre-Yves Chibon <pingou@pingoured.fr>

"""

from __future__ import unicode_literals, absolute_import

import logging

import flask

from cryptography.hazmat.primitives import constant_time
from kitchen.text.converters import to_bytes

import pagure
import pagure.exceptions
import pagure.lib.query
import pagure.lib.plugins
import pagure.lib.lib_ci as lib_ci
from pagure.api import API, APIERROR, api_method


_log = logging.getLogger(__name__)


@API.route(
    "/ci/jenkins/<repo>/<pagure_ci_token>/build-finished", methods=["POST"]
)
@API.route(
    "/ci/jenkins/<namespace>/<repo>/<pagure_ci_token>/build-finished",
    methods=["POST"],
)
@API.route(
    "/ci/jenkins/forks/<username>/<repo>/" "<pagure_ci_token>/build-finished",
    methods=["POST"],
)
@API.route(
    "/ci/jenkins/forks/<username>/<namespace>/<repo>/"
    "<pagure_ci_token>/build-finished",
    methods=["POST"],
)
@api_method
def jenkins_ci_notification(
    repo, pagure_ci_token, username=None, namespace=None
):
    """
    Jenkins Build Notification
    --------------------------
    At the end of a build on Jenkins, this URL is used (if the project is
    rightly configured) to flag a pull-request with the result of the build.

    ::

        POST /api/0/ci/jenkins/<repo>/<token>/build-finished

    """

    project = pagure.lib.query._get_project(
        flask.g.session, repo, user=username, namespace=namespace
    )
    flask.g.repo_locked = True
    flask.g.repo = project
    if not project:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT)

    ci_hook = pagure.lib.plugins.get_plugin("Pagure CI")
    ci_hook.db_object()

    if not constant_time.bytes_eq(
        to_bytes(pagure_ci_token), to_bytes(project.ci_hook.pagure_ci_token)
    ):
        raise pagure.exceptions.APIError(401, error_code=APIERROR.EINVALIDTOK)

    data = flask.request.get_json()
    if not data:
        _log.debug("Bad Request: No JSON retrieved")
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    build_id = data.get("build", {}).get("number")
    if not build_id:
        _log.debug("Bad Request: No build ID retrieved")
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    build_phase = data.get("build", {}).get("phase")
    if not build_phase:
        _log.debug("Bad Request: No build phase retrieved")
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)
    if build_phase not in ["STARTED", "FINALIZED"]:
        _log.debug(
            "Ignoring phase: %s - not in the list: STARTED, FINALIZED",
            build_phase,
        )
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    try:
        lib_ci.process_jenkins_build(flask.g.session, project, build_id)
    except pagure.exceptions.NoCorrespondingPR as err:
        raise pagure.exceptions.APIError(
            400, error_code=APIERROR.ENOCODE, error=str(err)
        )
    except pagure.exceptions.PagureException as err:
        _log.error("Error processing jenkins notification", exc_info=err)
        raise pagure.exceptions.APIError(
            400, error_code=APIERROR.ENOCODE, error=str(err)
        )

    _log.info("Successfully proccessed jenkins notification")
    return ("", 204)