예제 #1
0
파일: api.py 프로젝트: elsigh/dpxdt
def create_release(build):
    """Creates a new release candidate for a build."""
    release_name = request.form.get('release_name')
    utils.jsonify_assert(release_name, 'release_name required')
    url = request.form.get('url')
    utils.jsonify_assert(release_name, 'url required')

    release = models.Release(
        name=release_name,
        url=url,
        number=1,
        build_id=build.id)

    last_candidate = (
        models.Release.query
        .filter_by(build_id=build.id, name=release_name)
        .order_by(models.Release.number.desc())
        .first())
    if last_candidate:
        release.number += last_candidate.number

    db.session.add(release)
    db.session.commit()

    logging.info('Created release: build_id=%r, release_name=%r, url=%r, '
                 'release_number=%d', build.id, release.name,
                 url, release.number)

    return flask.jsonify(
        success=True,
        build_id=build.id,
        release_name=release.name,
        release_number=release.number,
        url=url)
예제 #2
0
파일: api.py 프로젝트: elsigh/dpxdt
def _find_last_good_run(build):
    """Finds the last good release and run for a build."""
    run_name = request.form.get('run_name', type=str)
    utils.jsonify_assert(run_name, 'run_name required')

    last_good_release = (
        models.Release.query
        .filter_by(
            build_id=build.id,
            status=models.Release.GOOD)
        .order_by(models.Release.created.desc())
        .first())

    last_good_run = None

    if last_good_release:
        logging.debug('Found last good release for: build_id=%r, '
                      'release_name=%r, release_number=%d',
                      build.id, last_good_release.name,
                      last_good_release.number)
        last_good_run = (
            models.Run.query
            .filter_by(release_id=last_good_release.id, name=run_name)
            .first())
        if last_good_run:
            logging.debug('Found last good run for: build_id=%r, '
                          'release_name=%r, release_number=%d, '
                          'run_name=%r',
                          build.id, last_good_release.name,
                          last_good_release.number, last_good_run.name)

    return last_good_release, last_good_run
예제 #3
0
def can_api_key_access_build(param_name):
    """Determines if the current API key can access the build in the request.

    Args:
        param_name: Parameter name to use for getting the build ID from the
            request. Will fetch from GET or POST requests.

    Returns:
        (api_key, build) The API Key and the Build it has access to.
    """
    build_id = (request.args.get(param_name, type=int)
                or request.form.get(param_name, type=int))
    utils.jsonify_assert(build_id, 'build_id required')

    if app.config.get('IGNORE_AUTH'):
        api_key = models.ApiKey(id='anonymous_superuser',
                                secret='',
                                superuser=True)
        build = models.Build.query.get(build_id)
        utils.jsonify_assert(build is not None, 'build must exist', 404)
    else:
        ops = _get_api_key_ops()
        api_key, build = ops.can_access_build(build_id)

    return api_key, build
예제 #4
0
파일: api.py 프로젝트: elsigh/dpxdt
def _get_release_params():
    """Gets the release params from the current request."""
    release_name = request.form.get('release_name')
    utils.jsonify_assert(release_name, 'release_name required')
    release_number = request.form.get('release_number', type=int)
    utils.jsonify_assert(release_number is not None, 'release_number required')
    return release_name, release_number
예제 #5
0
def _get_or_create_run(build):
    """Gets a run for a build or creates it if it does not exist."""
    release_name, release_number = _get_release_params()
    run_name = request.form.get('run_name', type=str)
    utils.jsonify_assert(run_name, 'run_name required')

    release = (
        models.Release.query
        .filter_by(build_id=build.id, name=release_name, number=release_number)
        .first())
    utils.jsonify_assert(release, 'release does not exist')

    run = (
        models.Run.query
        .filter_by(release_id=release.id, name=run_name)
        .first())
    if not run:
        # Ignore re-reports of the same run name for this release.
        logging.info('Created run: build_id=%r, release_name=%r, '
                     'release_number=%d, run_name=%r',
                     build.id, release.name, release.number, run_name)
        run = models.Run(
            release_id=release.id,
            name=run_name,
            status=models.Run.DATA_PENDING)
        db.session.add(run)
        db.session.flush()

    return release, run
예제 #6
0
def runs_done():
    """Marks a release candidate as having all runs reported."""
    build = g.build
    release_name, release_number = _get_release_params()

    release = (
        models.Release.query
        .filter_by(build_id=build.id, name=release_name, number=release_number)
        .with_lockmode('update')
        .first())
    utils.jsonify_assert(release, 'Release does not exist')

    release.status = models.Release.PROCESSING
    db.session.add(release)
    _check_release_done_processing(release)
    db.session.commit()

    signals.release_updated_via_api.send(app, build=build, release=release)

    logging.info('Runs done for release: build_id=%r, release_name=%r, '
                 'release_number=%d', build.id, release.name, release.number)

    results_url = url_for(
        'view_release',
        id=build.id,
        name=release.name,
        number=release.number,
        _external=True)

    return flask.jsonify(
        success=True,
        results_url=results_url)
예제 #7
0
def _find_last_good_run(build):
    """Finds the last good release and run for a build."""
    run_name = request.form.get('run_name', type=str)
    utils.jsonify_assert(run_name, 'run_name required')

    last_good_release = (
        models.Release.query
        .filter_by(
            build_id=build.id,
            status=models.Release.GOOD)
        .order_by(models.Release.created.desc())
        .first())

    last_good_run = None

    if last_good_release:
        logging.debug('Found last good release for: build_id=%r, '
                      'release_name=%r, release_number=%d',
                      build.id, last_good_release.name,
                      last_good_release.number)
        last_good_run = (
            models.Run.query
            .filter_by(release_id=last_good_release.id, name=run_name)
            .first())
        if last_good_run:
            logging.debug('Found last good run for: build_id=%r, '
                          'release_name=%r, release_number=%d, '
                          'run_name=%r',
                          build.id, last_good_release.name,
                          last_good_release.number, last_good_run.name)

    return last_good_release, last_good_run
예제 #8
0
파일: api.py 프로젝트: m8ttyB/dpxdt
def create_release():
    """Creates a new release candidate for a build."""
    build_id = request.form.get('build_id', type=int)
    utils.jsonify_assert(build_id is not None, 'build_id required')
    release_name = request.form.get('release_name')
    utils.jsonify_assert(release_name, 'release_name required')
    # TODO: Make sure build_id exists
    # TODO: Make sure requesting user is owner of the build_id

    release = models.Release(
        name=release_name,
        number=1,
        build_id=build_id)

    last_candidate = (
        models.Release.query
        .filter_by(build_id=build_id, name=release_name)
        .order_by(models.Release.number.desc())
        .first())
    if last_candidate:
        release.number += last_candidate.number

    db.session.add(release)
    db.session.commit()

    logging.info('Created release: build_id=%r, release_name=%r, '
                 'release_number=%d', build_id, release_name, release.number)

    return flask.jsonify(
        build_id=build_id,
        release_name=release_name,
        release_number=release.number)
예제 #9
0
파일: api.py 프로젝트: elsigh/dpxdt
def _get_or_create_run(build):
    """Gets a run for a build or creates it if it does not exist."""
    release_name, release_number = _get_release_params()
    run_name = request.form.get('run_name', type=str)
    utils.jsonify_assert(run_name, 'run_name required')

    release = (
        models.Release.query
        .filter_by(build_id=build.id, name=release_name, number=release_number)
        .first())
    utils.jsonify_assert(release, 'release does not exist')

    run = (
        models.Run.query
        .filter_by(release_id=release.id, name=run_name)
        .first())
    if not run:
        # Ignore re-reports of the same run name for this release.
        logging.info('Created run: build_id=%r, release_name=%r, '
                     'release_number=%d, run_name=%r',
                     build.id, release.name, release.number, run_name)
        run = models.Run(
            release_id=release.id,
            name=run_name,
            status=models.Run.DATA_PENDING)
        db.session.add(run)
        db.session.flush()

    return release, run
예제 #10
0
def _get_release_params():
    """Gets the release params from the current request."""
    release_name = request.form.get('release_name')
    utils.jsonify_assert(release_name, 'release_name required')
    release_number = request.form.get('release_number', type=int)
    utils.jsonify_assert(release_number is not None, 'release_number required')
    return release_name, release_number
예제 #11
0
파일: api.py 프로젝트: m8ttyB/dpxdt
def upload():
    """Uploads an artifact referenced by a run."""
    # TODO: Require an API key on the basic auth header
    utils.jsonify_assert(len(request.files) == 1,
                         'Need exactly one uploaded file')

    file_storage = request.files.values()[0]
    data = file_storage.read()
    sha1sum = hashlib.sha1(data).hexdigest()
    exists = models.Artifact.query.filter_by(id=sha1sum).first()
    if exists:
        logging.info('Upload already exists: artifact_id=%r', sha1sum)
        return flask.jsonify(sha1sum=sha1sum)

    # TODO: Mark that this owner/build has access to this sha1sum, to prevent
    # users from pointing at sha1sums of images they don't own? Maybe too
    # paranoid.

    content_type, _ = mimetypes.guess_type(file_storage.filename)
    artifact = models.Artifact(
        id=sha1sum,
        content_type=content_type,
        data=data)
    db.session.add(artifact)
    db.session.commit()

    logging.info('Upload received: artifact_id=%r, content_type=%r',
                 sha1sum, content_type)
    return flask.jsonify(sha1sum=sha1sum)
예제 #12
0
파일: api.py 프로젝트: elsigh/dpxdt
def runs_done(build):
    """Marks a release candidate as having all runs reported."""
    release_name, release_number = _get_release_params()

    release = (
        models.Release.query
        .filter_by(build_id=build.id, name=release_name, number=release_number)
        .first())
    utils.jsonify_assert(release, 'Release does not exist')

    release.status = models.Release.PROCESSING
    db.session.add(release)
    _check_release_done_processing(release)
    db.session.commit()

    logging.info('Runs done for release: build_id=%r, release_name=%r, '
                 'release_number=%d', build.id, release.name, release.number)

    results_url = url_for(
        'view_release',
        id=build.id,
        name=release.name,
        number=release.number,
        _external=True)

    return flask.jsonify(
        success=True,
        results_url=results_url)
예제 #13
0
    def wrapped(*args, **kwargs):
        api_key = current_api_key()
        g.api_key = api_key

        utils.jsonify_assert(api_key.superuser,
                             'API key=%r must be a super user' % api_key.id,
                             403)

        return f(*args, **kwargs)
예제 #14
0
파일: auth.py 프로젝트: CurtainDog/dpxdt
    def wrapped(*args, **kwargs):
        api_key = current_api_key()

        utils.jsonify_assert(
            api_key.superuser,
            'API key=%r must be a super user' % api_key.id,
            403)

        return f(*args, **kwargs)
예제 #15
0
파일: api.py 프로젝트: m8ttyB/dpxdt
def create_build():
    """Creates a new build for a user."""
    # TODO: Make sure the requesting user is logged in
    name = request.form.get('name')
    utils.jsonify_assert(name, 'name required')

    build = models.Build(name=name)
    db.session.add(build)
    db.session.commit()

    logging.info('Created build: build_id=%r, name=%r', build.id, name)

    return flask.jsonify(build_id=build.id, name=name)
예제 #16
0
def create_release():
    """Creates a new release candidate for a build."""
    build = g.build
    release_name = request.form.get('release_name')
    utils.jsonify_assert(release_name, 'release_name required')
    url = request.form.get('url')
    utils.jsonify_assert(release_name, 'url required')

    release = models.Release(
        name=release_name,
        url=url,
        number=1,
        build_id=build.id)

    last_candidate = (
        models.Release.query
        .filter_by(build_id=build.id, name=release_name)
        .order_by(models.Release.number.desc())
        .first())
    if last_candidate:
        release.number += last_candidate.number
        if last_candidate.status == models.Release.PROCESSING:
            canceled_task_count = work_queue.cancel(
                release_id=last_candidate.id)
            logging.info('Canceling %d tasks for previous attempt '
                         'build_id=%r, release_name=%r, release_number=%d',
                         canceled_task_count, build.id, last_candidate.name,
                         last_candidate.number)
            last_candidate.status = models.Release.BAD
            db.session.add(last_candidate)

    db.session.add(release)
    db.session.commit()

    signals.release_updated_via_api.send(app, build=build, release=release)

    logging.info('Created release: build_id=%r, release_name=%r, url=%r, '
                 'release_number=%d', build.id, release.name,
                 url, release.number)

    return flask.jsonify(
        success=True,
        build_id=build.id,
        release_name=release.name,
        release_number=release.number,
        url=url)
예제 #17
0
파일: api.py 프로젝트: elsigh/dpxdt
def upload(build):
    """Uploads an artifact referenced by a run."""
    utils.jsonify_assert(len(request.files) == 1,
                         'Need exactly one uploaded file')

    file_storage = request.files.values()[0]
    data = file_storage.read()
    content_type, _ = mimetypes.guess_type(file_storage.filename)

    artifact = _save_artifact(build, data, content_type)

    db.session.add(artifact)
    db.session.commit()

    return flask.jsonify(
        success=True,
        build_id=build.id,
        sha1sum=artifact.id,
        content_type=content_type)
예제 #18
0
def upload():
    """Uploads an artifact referenced by a run."""
    build = g.build
    utils.jsonify_assert(len(request.files) == 1,
                         'Need exactly one uploaded file')

    file_storage = request.files.values()[0]
    data = file_storage.read()
    content_type, _ = mimetypes.guess_type(file_storage.filename)

    artifact = _save_artifact(build, data, content_type)

    db.session.add(artifact)
    db.session.commit()

    return flask.jsonify(
        success=True,
        build_id=build.id,
        sha1sum=artifact.id,
        content_type=content_type)
예제 #19
0
파일: api.py 프로젝트: CurtainDog/dpxdt
def request_run(build):
    """Requests a new run for a release candidate."""
    current_release, current_run = _get_or_create_run(build)
    last_good_release, last_good_run = _find_last_good_run(build)

    if last_good_run:
        current_run.ref_url = last_good_run.url
        current_run.ref_image = last_good_run.image
        current_run.ref_log = last_good_run.log
        current_run.ref_config = last_good_run.config

    current_url = request.form.get('url', type=str)
    config_data = request.form.get('config', default='{}', type=str)
    utils.jsonify_assert(current_url, 'url to capture required')
    utils.jsonify_assert(config_data, 'config document required')

    # Validate the JSON config parses.
    try:
        config_dict = json.loads(config_data)
    except Exception, e:
        return jsonify_error(e)
예제 #20
0
파일: auth.py 프로젝트: atbrox/dpxdt
def current_api_key():
    """Determines the API key for the current request.

    Returns:
        The API key.
    """
    if app.config.get('IGNORE_AUTH'):
        return models.ApiKey(
            id='anonymous_superuser',
            secret='',
            superuser=True)

    auth_header = request.authorization
    if not auth_header:
        logging.debug('API request lacks authorization header')
        abort(flask.Response(
            'API key required', 401,
            {'WWW-Authenticate': 'Basic realm="API key required"'}))

    api_key = models.ApiKey.query.get(auth_header.username)
    utils.jsonify_assert(api_key, 'API key must exist', 403)
    utils.jsonify_assert(api_key.active, 'API key must be active', 403)
    utils.jsonify_assert(api_key.secret == auth_header.password,
                         'Must have good credentials', 403)

    logging.debug('Authenticated as API key=%r', api_key.id)

    return api_key
예제 #21
0
파일: api.py 프로젝트: m8ttyB/dpxdt
def release_done():
    """Marks a release candidate as good or bad."""
    build_id, release_name, release_number = _get_release_params()
    status = request.form.get('status')
    valid_statuses = (models.Release.GOOD, models.Release.BAD)
    utils.jsonify_assert(status in valid_statuses,
                         'status must be in %r' % valid_statuses)

    release = (
        models.Release.query
        .filter_by(build_id=build_id, name=release_name, number=release_number)
        .first())
    utils.jsonify_assert(release, 'Release does not exist')

    release.status = status
    db.session.add(release)
    db.session.commit()

    logging.info('Release marked as %s: build_id=%r, release_name=%r, '
                 'number=%d', status, build_id, release_name, release_number)

    return flask.jsonify(success=True)
예제 #22
0
def request_run():
    """Requests a new run for a release candidate."""
    build = g.build
    current_release, current_run = _get_or_create_run(build)

    current_url = request.form.get('url', type=str)
    config_data = request.form.get('config', default='{}', type=str)
    utils.jsonify_assert(current_url, 'url to capture required')
    utils.jsonify_assert(config_data, 'config document required')

    config_artifact = _enqueue_capture(
        build, current_release, current_run, current_url, config_data)

    ref_url = request.form.get('ref_url', type=str)
    ref_config_data = request.form.get('ref_config', type=str)
    utils.jsonify_assert(
        bool(ref_url) == bool(ref_config_data),
        'ref_url and ref_config must both be specified or not specified')

    if ref_url and ref_config_data:
        ref_config_artifact = _enqueue_capture(
            build, current_release, current_run, ref_url, ref_config_data,
            baseline=True)
    else:
        _, last_good_run = _find_last_good_run(build)
        if last_good_run:
            current_run.ref_url = last_good_run.url
            current_run.ref_image = last_good_run.image
            current_run.ref_log = last_good_run.log
            current_run.ref_config = last_good_run.config

    db.session.add(current_run)
    db.session.commit()

    signals.run_updated_via_api.send(
        app, build=build, release=current_release, run=current_run)

    return flask.jsonify(
        success=True,
        build_id=build.id,
        release_name=current_release.name,
        release_number=current_release.number,
        run_name=current_run.name,
        url=current_run.url,
        config=current_run.config,
        ref_url=current_run.ref_url,
        ref_config=current_run.ref_config)
예제 #23
0
파일: api.py 프로젝트: atbrox/dpxdt
def request_run():
    """Requests a new run for a release candidate."""
    build = g.build
    current_release, current_run = _get_or_create_run(build)

    current_url = request.form.get('url', type=str)
    config_data = request.form.get('config', default='{}', type=str)
    utils.jsonify_assert(current_url, 'url to capture required')
    utils.jsonify_assert(config_data, 'config document required')

    config_artifact = _enqueue_capture(
        build, current_release, current_run, current_url, config_data)

    ref_url = request.form.get('ref_url', type=str)
    ref_config_data = request.form.get('ref_config', type=str)
    utils.jsonify_assert(
        bool(ref_url) == bool(ref_config_data),
        'ref_url and ref_config must both be specified or not specified')

    if ref_url and ref_config_data:
        ref_config_artifact = _enqueue_capture(
            build, current_release, current_run, ref_url, ref_config_data,
            baseline=True)
    else:
        _, last_good_run = _find_last_good_run(build)
        if last_good_run:
            current_run.ref_url = last_good_run.url
            current_run.ref_image = last_good_run.image
            current_run.ref_log = last_good_run.log
            current_run.ref_config = last_good_run.config

    db.session.add(current_run)
    db.session.commit()

    return flask.jsonify(
        success=True,
        build_id=build.id,
        release_name=current_release.name,
        release_number=current_release.number,
        run_name=current_run.name,
        url=current_run.url,
        config=current_run.config,
        ref_url=current_run.ref_url,
        ref_config=current_run.ref_config)
예제 #24
0
파일: auth.py 프로젝트: jujugrrr/dpxdt
def can_api_key_access_build(param_name):
    """Determines if the current API key can access the build in the request.

    Args:
        param_name: Parameter name to use for getting the build ID from the
            request. Will fetch from GET or POST requests.

    Returns:
        The Build the API key has access to.
    """
    api_key = current_api_key()
    build_id = request.args.get(param_name, type=int) or request.form.get(param_name, type=int)
    utils.jsonify_assert(build_id, "build_id required")
    build = models.Build.query.get(build_id)
    utils.jsonify_assert(build is not None, "build must exist", 404)

    if not api_key.superuser:
        utils.jsonify_assert(api_key.build_id == build_id, "API key must have access", 404)

    return build
예제 #25
0
파일: api.py 프로젝트: m8ttyB/dpxdt
def report_pdiff():
    """Reports a pdiff for a run.

    When there is no diff to report, supply the "no_diff" parameter.
    """
    build_id, release_name, release_number = _get_release_params()
    run_name = request.form.get('run_name', type=str)
    utils.jsonify_assert(run_name, 'run_name required')

    release = (
        models.Release.query
        .filter_by(
            build_id=build_id,
            name=release_name)
        .first())
    utils.jsonify_assert(release, 'Release does not exist')
    run = (
        models.Run.query
        .filter_by(release_id=release.id, name=run_name)
        .first())
    utils.jsonify_assert(release, 'Run does not exist')

    no_diff = request.form.get('no_diff')
    run.needs_diff = not (no_diff or run.diff_image or run.diff_log)
    run.diff_image = request.form.get('diff_image', type=str)
    run.diff_log = request.form.get('diff_log', type=str)

    db.session.add(run)

    logging.info('Saved pdiff: build_id=%r, release_name=%r, '
                 'release_number=%d, run_name=%r, '
                 'no_diff=%r, diff_image=%r, diff_log=%r',
                 build_id, release_name, release_number, run_name,
                 no_diff, run.diff_image, run.diff_log)

    _check_release_done_processing(run.release_id)
    db.session.commit()

    return flask.jsonify(success=True)
예제 #26
0
파일: api.py 프로젝트: m8ttyB/dpxdt
def report_run():
    """Reports a new run for a release candidate."""
    build_id, release_name, release_number = _get_release_params()
    run_name = request.form.get('run_name', type=str)
    utils.jsonify_assert(run_name, 'run_name required')

    release = (
        models.Release.query
        .filter_by(build_id=build_id, name=release_name, number=release_number)
        .first())
    utils.jsonify_assert(release, 'release does not exist')
    # TODO: Make sure requesting user is owner of the build_id

    current_image = request.form.get('image', type=str)
    utils.jsonify_assert(current_image, 'image must be supplied')
    current_log = request.form.get('log', type=str)
    current_config = request.form.get('config', type=str)
    no_diff = request.form.get('no_diff')
    diff_image = request.form.get('diff_image', type=str)
    diff_log = request.form.get('diff_log', type=str)
    needs_diff = not (no_diff or diff_image or diff_log)

    # Find the previous corresponding run and automatically connect it.
    last_good_release = (
        models.Release.query
        .filter_by(
            build_id=build_id,
            status=models.Release.GOOD)
        .order_by(models.Release.created.desc())
        .first())
    previous_id = None
    last_image = None
    if last_good_release:
        logging.debug('Found last good release for: build_id=%r, '
                      'release_name=%r, release_number=%d, '
                      'last_good_release_id=%d',
                      build_id, release_name, release_number,
                      last_good_release.id)
        last_good_run = (
            models.Run.query
            .filter_by(release_id=last_good_release.id, name=run_name)
            .first())
        if last_good_run:
            logging.debug('Found last good run for: build_id=%r, '
                          'release_name=%r, release_number=%d, '
                          'last_good_release_id=%d, last_good_run_id=%r, '
                          'last_good_image=%r',
                          build_id, release_name, release_number,
                          last_good_release.id, last_good_run.id,
                          last_good_run.image)
            previous_id = last_good_run.id
            last_image = last_good_run.image

    run = models.Run(
        name=run_name,
        release_id=release.id,
        image=current_image,
        log=current_log,
        config=current_config,
        previous_id=previous_id,
        needs_diff=bool(needs_diff and last_image),
        diff_image=diff_image,
        diff_log=diff_log)
    db.session.add(run)
    db.session.flush()

    # Schedule pdiff if there isn't already an image.
    if needs_diff and last_image:
        # TODO: Move this queue name to a flag.
        work_queue.add('run-pdiff', dict(
            build_id=build_id,
            release_name=release_name,
            release_number=release_number,
            run_name=run_name,
            reference_sha1sum=current_image,
            run_sha1sum=last_image,
        ))

    db.session.commit()

    logging.info('Created run: build_id=%r, release_name=%r, '
                 'release_number=%d, run_name=%r',
                 build_id, release_name, release_number, run_name)

    return flask.jsonify(success=True)