Ejemplo n.º 1
0
def test_hash_with_different_list_order_is_equal():
    a = LandingAssessment.hash_warning_list(
        [
            {
                'id': 'W1',
                'message': 'oops 1'
            },
            {
                'id': 'W2',
                'message': 'oops 2'
            },
            {
                'id': 'W3',
                'message': 'oops 3'
            },
        ]
    )
    b = LandingAssessment.hash_warning_list(
        [
            {
                'id': 'W3',
                'message': 'oops 3'
            },
            {
                'id': 'W2',
                'message': 'oops 2'
            },
            {
                'id': 'W1',
                'message': 'oops 1'
            },
        ]
    )
    assert a == b
Ejemplo n.º 2
0
def test_construct_assessment_dict_only_blockers():
    warnings = []
    blockers = [MockProblem('oops')]
    assessment = LandingAssessment(warnings, blockers)
    result = assessment.to_dict()
    assert result['confirmation_token'] is None
    assert result['blockers'][0]['message'] == 'oops'
    assert not result['warnings']
Ejemplo n.º 3
0
def test_construct_assessment_dict_no_warnings_or_blockers():
    assessment = LandingAssessment([], [])
    expected_dict = {
        'confirmation_token': None,
        'warnings': [],
        'blockers': [],
    }

    assert assessment.to_dict() == expected_dict
Ejemplo n.º 4
0
def test_hash_with_same_id_different_warning_details_are_different():
    a = LandingAssessment.hash_warning_list([{
        'id': 'W0',
        'message': 'revision 5 problem'
    }])
    b = LandingAssessment.hash_warning_list([{
        'id': 'W0',
        'message': 'revision 8 problem'
    }])
    assert a != b
Ejemplo n.º 5
0
def test_hash_with_duplicate_ids_are_not_stripped():
    a = LandingAssessment.hash_warning_list([
        {
            'id': 'W0',
            'message': 'same'
        },
        {
            'id': 'W0',
            'message': 'same'
        },
    ])
    b = LandingAssessment.hash_warning_list([{'id': 'W0', 'message': 'same'}])
    assert a != b
Ejemplo n.º 6
0
def test_token_with_warnings_is_not_none():
    assert LandingAssessment.hash_warning_list(
        [{
            'id': 'W0',
            'message': 'oops'
        }]
    )
Ejemplo n.º 7
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
Ejemplo n.º 8
0
def test_token_for_no_issues_is_none():
    assert LandingAssessment.hash_warning_list([]) is None
Ejemplo n.º 9
0
def test_hash_object_throws_error():
    with pytest.raises(KeyError):
        LandingAssessment.hash_warning_list([{'id': object()}])
Ejemplo n.º 10
0
def test_hash_of_empty_list_is_None():
    assert LandingAssessment.hash_warning_list([]) is None