Пример #1
0
def test_land_failed_revision(db, client, auth0_mock, phabdouble, s3,
                              transfactory, status):
    diff = phabdouble.diff()
    revision = phabdouble.revision(diff=diff, repo=phabdouble.repo())
    phabdouble.reviewer(revision, phabdouble.user(username='******'))
    _create_transplant(
        db,
        revision_id=revision['id'],
        diff_id=diff['id'],
        status=status,
    )
    transfactory.mock_successful_response(2)

    response = client.post('/landings',
                           data=json.dumps({
                               'revision_id':
                               'D{}'.format(revision['id']),
                               'diff_id':
                               diff['id'],
                           }),
                           headers=auth0_mock.mock_headers,
                           content_type='application/json')
    assert response.status_code == 202

    # Ensure DB access isn't using uncommitted data.
    db.session.close()

    assert Transplant.is_revision_submitted(revision['id'])
Пример #2
0
    def check(cls, *, revision_id, diff_id, **kwargs):
        already_submitted = Transplant.is_revision_submitted(revision_id)
        if not already_submitted:
            return None

        submit_diff = already_submitted.revision_to_diff_id[str(revision_id)]

        if diff_id == submit_diff:
            return cls(
                'This revision is already queued for landing with '
                'the same diff.'
            )
        else:
            return cls(
                'This revision is already queued for landing with '
                'diff {}'.format(submit_diff)
            )
Пример #3
0
def post(data):
    """API endpoint at POST /landings to land revision."""
    logger.info(
        'landing requested by user',
        extra={
            'path': request.path,
            'method': request.method,
            'data': data,
        }
    )

    revision_id, diff_id = unmarshal_landing_request(data)
    confirmation_token = data.get('confirmation_token') or None

    phab = g.phabricator

    get_revision = lazy_get_revision(phab, revision_id)
    get_latest_diff = lazy_get_latest_diff(phab, get_revision)
    get_diff = lazy_get_diff(phab, diff_id, get_latest_diff)
    get_diff_author = lazy_get_diff_author(get_diff)
    get_latest_landed = lazy(Transplant.legacy_latest_landed)(revision_id)
    get_repository = lazy_get_repository(phab, get_revision)
    get_landing_repo = lazy_get_landing_repo(
        get_repository, current_app.config.get('ENVIRONMENT')
    )
    get_open_parents = lazy_get_open_parents(phab, get_revision)
    get_reviewers = lazy_get_reviewers(get_revision)
    get_reviewer_info = lazy_reviewers_search(phab, get_reviewers)
    get_reviewers_extra_state = lazy_get_reviewers_extra_state(
        get_reviewers, get_diff
    )
    get_revision_status = lazy_get_revision_status(get_revision)
    assessment = check_landing_conditions(
        g.auth0_user,
        revision_id,
        diff_id,
        get_revision,
        get_latest_diff,
        get_latest_landed,
        get_repository,
        get_landing_repo,
        get_diff,
        get_diff_author,
        get_open_parents,
        get_reviewers,
        get_reviewer_info,
        get_reviewers_extra_state,
        get_revision_status,
        short_circuit=True,
    )
    assessment.raise_if_blocked_or_unacknowledged(confirmation_token)
    if assessment.warnings:
        # Log any warnings that were acknowledged, for auditing.
        logger.info(
            'Landing with acknowledged warnings is being requested',
            extra={
                'revision_id': revision_id,
                'warnings': [w.serialize() for w in assessment.warnings],
            }
        )

    # These are guaranteed to return proper data since we're
    # running after checking_landing_conditions().
    revision = get_revision()
    landing_repo = get_landing_repo()
    author_name, author_email = get_diff_author()

    # Collect the usernames of reviewers who have accepted.
    reviewers = get_reviewers()
    users, projects = get_reviewer_info()
    accepted_reviewers = [
        reviewer_identity(phid, users, projects).identifier
        for phid, r in reviewers.items()
        if r['status'] is ReviewerStatus.ACCEPTED
    ]

    # Seconds since Unix Epoch, UTC.
    date_modified = phab.expect(revision, 'fields', 'dateModified')

    title = phab.expect(revision, 'fields', 'title')
    summary = phab.expect(revision, 'fields', 'summary')
    bug_id = get_bugzilla_bug(revision)
    human_revision_id = 'D{}'.format(revision_id)
    revision_url = urllib.parse.urljoin(
        current_app.config['PHABRICATOR_URL'], human_revision_id
    )
    commit_message = format_commit_message(
        title, bug_id, accepted_reviewers, summary, revision_url
    )

    # Construct the patch that will be sent to transplant.
    raw_diff = phab.call_conduit('differential.getrawdiff', diffID=diff_id)
    patch = build_patch_for_revision(
        raw_diff, author_name, author_email, commit_message[1], date_modified
    )

    # Upload the patch to S3
    patch_url = upload(
        revision_id,
        diff_id,
        patch,
        current_app.config['PATCH_BUCKET_NAME'],
        aws_access_key=current_app.config['AWS_ACCESS_KEY'],
        aws_secret_key=current_app.config['AWS_SECRET_KEY'],
    )

    trans = TransplantClient(
        current_app.config['TRANSPLANT_URL'],
        current_app.config['TRANSPLANT_USERNAME'],
        current_app.config['TRANSPLANT_PASSWORD'],
    )

    submitted_assessment = LandingAssessment(
        blockers=[
            LandingInProgress(
                'This revision was submitted for landing by another user at '
                'the same time.'
            )
        ]
    )
    ldap_username = g.auth0_user.email

    try:
        # WARNING: Entering critical section, do not add additional
        # code unless absolutely necessary. Acquires a lock on the
        # transplants table which gives exclusive write access and
        # prevents readers who are entering this critical section.
        # See https://www.postgresql.org/docs/9.3/static/explicit-locking.html
        # for more details on the specifics of the lock mode.
        with db.session.begin_nested():
            db.session.execute(
                'LOCK TABLE transplants IN SHARE ROW EXCLUSIVE MODE;'
            )
            if Transplant.is_revision_submitted(revision_id):
                submitted_assessment.raise_if_blocked_or_unacknowledged(None)

            transplant_request_id = trans.land(
                revision_id=revision_id,
                ldap_username=ldap_username,
                patch_urls=[patch_url],
                tree=landing_repo.tree,
                pingback=current_app.config['PINGBACK_URL'],
                push_bookmark=landing_repo.push_bookmark
            )
            transplant = Transplant(
                request_id=transplant_request_id,
                revision_to_diff_id={str(revision_id): diff_id},
                revision_order=[str(revision_id)],
                requester_email=ldap_username,
                tree=landing_repo.tree,
                repository_url=landing_repo.url,
                status=TransplantStatus.submitted
            )
            db.session.add(transplant)
    except TransplantError as exc:
        logger.info(
            'error creating transplant',
            extra={'revision': revision_id},
            exc_info=exc
        )
        return problem(
            502,
            'Landing not created',
            'The requested revision does exist, but landing failed.'
            'Please retry your request at a later time.',
            type='https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502'
        )

    # Transaction succeeded, commit the session.
    db.session.commit()

    logger.info(
        'transplant created',
        extra={
            'revision_id': revision_id,
            'transplant_id': transplant.id,
        }
    )
    return {'id': transplant.id}, 202
Пример #4
0
def test_revision_not_submitted(db, status):
    _create_transplant(db, status=status)
    assert not Transplant.is_revision_submitted(1)
Пример #5
0
def test_revision_already_submitted(db, status, considered_submitted):
    landing = _create_transplant(db, status=status, diff_id=2)
    if considered_submitted:
        assert Transplant.is_revision_submitted(1) == landing
    else:
        assert not Transplant.is_revision_submitted(1)