示例#1
0
async def test_dispatch():
    """
    Test message dispatch from a queue to 2 queues
    """
    bus = MessageBus()
    bus.add_queue("input")
    # limit size to immediately stop execution for unit test
    bus.add_queue("output1", maxsize=3)
    bus.add_queue("output2", maxsize=3)
    assert isinstance(bus.queues["input"], asyncio.Queue)
    assert isinstance(bus.queues["output1"], asyncio.Queue)
    assert isinstance(bus.queues["output2"], asyncio.Queue)
    assert bus.queues["input"].qsize() == 0
    assert bus.queues["output1"].qsize() == 0
    assert bus.queues["output2"].qsize() == 0

    await bus.send("input", 1)
    await bus.send("input", 2)

    # Convert all strings from input in uppercase
    assert bus.queues["input"].qsize() == 2
    task = asyncio.create_task(bus.dispatch("input", ["output1", "output2"]))

    await bus.receive("output1") == 1
    await bus.receive("output2") == 1
    await bus.receive("output1") == 2
    await bus.receive("output2") == 2
    task.cancel()
    assert bus.queues["input"].qsize() == 0
    assert bus.queues["output1"].qsize() == 0
    assert bus.queues["output2"].qsize() == 0
示例#2
0
async def test_conversion():
    """
    Test message conversion between 2 queues
    """
    bus = MessageBus()
    bus.add_queue("input")
    bus.add_queue(
        "output",
        maxsize=3)  # limit size to immediately stop execution for unit test
    assert isinstance(bus.queues["input"], asyncio.Queue)
    assert isinstance(bus.queues["output"], asyncio.Queue)
    assert bus.queues["input"].qsize() == 0
    assert bus.queues["output"].qsize() == 0

    await bus.send("input", "test x")
    await bus.send("input", "hello world.")
    await bus.send("output", "lowercase")

    # Convert all strings from input in uppercase
    assert bus.queues["input"].qsize() == 2
    task = asyncio.create_task(
        bus.run(lambda x: x.upper(), "input", ["output"]))

    await bus.receive("output") == "lowercase"
    await bus.receive("output") == "TEST X"
    await bus.receive("output") == "HELLO WORLD."
    task.cancel()
    assert bus.queues["input"].qsize() == 0
    assert bus.queues["output"].qsize() == 0
示例#3
0
async def test_start_test_selection(PhabricatorMock, mock_taskcluster):
    bus = MessageBus()
    bus.add_queue(QUEUE_MONITORING_COMMUNITY)

    build = PhabricatorBuild(
        MockRequest(
            diff="125397",
            repo="PHID-REPO-saax4qdxlbbhahhp2kg5",
            revision="36474",
            target="PHID-HMBT-icusvlfibcebizyd33op",
        ))

    taskcluster_config.secrets["test_selection_enabled"] = True
    taskcluster_config.secrets["test_selection_share"] = 1.0

    with PhabricatorMock as phab:
        phab.load_patches_stack(build)
        phab.update_state(build)
        phab.load_reviewers(build)

        bugbug_utils = BugbugUtils(phab.api)
        bugbug_utils.register(bus)

    with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps:

        def trigger_hook_callback(request):
            payload = json.loads(request.body)
            assert payload == {
                "DIFF_ID":
                125397,
                "PHABRICATOR_DEPLOYMENT":
                "prod",
                "RUNNABLE_JOBS":
                "http://taskcluster.test/api/index/v1/task/gecko.v2.try.revision.MyRevision.firefox.decision/artifacts/public%2Frunnable-jobs.json",
            }
            return (
                200,
                {
                    "Content-Type": "application/json"
                },
                json.dumps({"status": {
                    "taskId": "xxx"
                }}),
            )

        rsps.add_callback(
            responses.POST,
            "http://community_taskcluster.test/api/hooks/v1/hooks/project-relman/bugbug-test-select/trigger",
            callback=trigger_hook_callback,
        )

        await bugbug_utils.start_test_selection(build, "MyRevision")

        group_id, hook_id, task_id = await bus.receive(
            QUEUE_MONITORING_COMMUNITY)
        assert group_id == "project-relman"
        assert hook_id == "bugbug-test-select"
        assert task_id == "xxx"
示例#4
0
async def test_failure_general(PhabricatorMock, mock_mc):
    """
    Run mercurial worker on a single diff
    and check the treeherder link publication as an artifact
    Use a Python common exception to trigger a broken build
    """
    diff = {
        "phid": "PHID-DIFF-test123",
        "id": 1234,
        "baseRevision": None,
        "revisionPHID": "PHID-DREV-deadbeef",
    }
    build = MockBuild(1234, "PHID-REPO-mc", 5678, "PHID-somehash", diff)
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    bus = MessageBus()
    bus.add_queue("phabricator")

    # Get initial tip commit in repo
    initial = mock_mc.repo.tip()

    # The patched and config files should not exist at first
    repo_dir = mock_mc.repo.root().decode("utf-8")
    config = os.path.join(repo_dir, "try_task_config.json")
    target = os.path.join(repo_dir, "test.txt")
    assert not os.path.exists(target)
    assert not os.path.exists(config)

    # Raise an exception during the workflow to trigger a broken build
    def boom(*args):
        raise Exception("Boom")

    mock_mc.apply_build = boom

    worker = MercurialWorker(
        "mercurial", "phabricator", repositories={"PHID-REPO-mc": mock_mc}
    )
    worker.register(bus)
    assert len(worker.repositories) == 1

    await bus.send("mercurial", build)
    assert bus.queues["mercurial"].qsize() == 1
    task = asyncio.create_task(worker.run())

    # Check the unit result was published
    mode, out_build, details = await bus.receive("phabricator")
    assert mode == "fail:general"
    assert out_build == build
    assert details["duration"] > 0
    assert details["message"] == "Boom"
    task.cancel()

    # Clone should not be modified
    tip = mock_mc.repo.tip()
    assert tip.node == initial.node
示例#5
0
async def test_treeherder_link(PhabricatorMock, mock_mc):
    """
    Run mercurial worker on a single diff
    and check the treeherder link publication as an artifact
    """
    # Preload the build
    diff = {
        "phid": "PHID-DIFF-test123",
        "revisionPHID": "PHID-DREV-deadbeef",
        "id": 1234,
        "baseRevision": "abcdef12345",
    }
    build = MockBuild(1234, "PHID-REPO-mc", 5678, "PHID-HMBT-somehash", diff)
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    bus = MessageBus()
    bus.add_queue("phabricator")

    # Get initial tip commit in repo
    initial = mock_mc.repo.tip()

    # The patched and config files should not exist at first
    repo_dir = mock_mc.repo.root().decode("utf-8")
    config = os.path.join(repo_dir, "try_task_config.json")
    target = os.path.join(repo_dir, "test.txt")
    assert not os.path.exists(target)
    assert not os.path.exists(config)

    worker = MercurialWorker(
        "mercurial", "phabricator", repositories={"PHID-REPO-mc": mock_mc}
    )
    worker.register(bus)
    assert len(worker.repositories) == 1

    await bus.send("mercurial", build)
    assert bus.queues["mercurial"].qsize() == 1
    task = asyncio.create_task(worker.run())

    # Check the treeherder link was queued
    mode, out_build, details = await bus.receive("phabricator")
    tip = mock_mc.repo.tip()
    assert mode == "success"
    assert out_build == build
    assert details[
        "treeherder_url"
    ] == "https://treeherder.mozilla.org/#/jobs?repo=try&revision={}".format(
        tip.node.decode("utf-8")
    )
    assert details["revision"] == tip.node.decode("utf-8")
    task.cancel()

    # Tip should be updated
    assert tip.node != initial.node
示例#6
0
async def test_failure_mercurial(PhabricatorMock, mock_mc):
    """
    Run mercurial worker on a single diff
    and check the treeherder link publication as an artifact
    Apply a bad mercurial patch to trigger a mercurial fail
    """
    diff = {
        "revisionPHID": "PHID-DREV-666",
        "baseRevision": "missing",
        "phid": "PHID-DIFF-666",
        "id": 666,
    }
    build = MockBuild(1234, "PHID-REPO-mc", 5678, "PHID-build-666", diff)
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    bus = MessageBus()
    bus.add_queue("phabricator")

    # Get initial tip commit in repo
    initial = mock_mc.repo.tip()

    # The patched and config files should not exist at first
    repo_dir = mock_mc.repo.root().decode("utf-8")
    config = os.path.join(repo_dir, "try_task_config.json")
    target = os.path.join(repo_dir, "test.txt")
    assert not os.path.exists(target)
    assert not os.path.exists(config)

    worker = MercurialWorker(
        "mercurial", "phabricator", repositories={"PHID-REPO-mc": mock_mc}
    )
    worker.register(bus)
    assert len(worker.repositories) == 1

    await bus.send("mercurial", build)
    assert bus.queues["mercurial"].qsize() == 1
    task = asyncio.create_task(worker.run())

    # Check the treeherder link was queued
    mode, out_build, details = await bus.receive("phabricator")
    assert mode == "fail:mercurial"
    assert out_build == build
    assert details["duration"] > 0
    assert details["message"] == MERCURIAL_FAILURE
    task.cancel()

    # Clone should not be modified
    tip = mock_mc.repo.tip()
    assert tip.node == initial.node
示例#7
0
async def test_message_passing_mp():
    """
    Test sending & receiving messages on a multiprocessing queueu
    """
    bus = MessageBus()
    bus.add_queue("test", mp=True)
    assert isinstance(bus.queues["test"], multiprocessing.queues.Queue)

    await bus.send("test", {"payload": 1234})
    await bus.send("test", {"another": "deadbeef"})
    await bus.send("test", "covfefe")
    assert bus.queues["test"].qsize() == 3

    msg = await bus.receive("test")
    assert msg == {"payload": 1234}
    msg = await bus.receive("test")
    assert msg == {"another": "deadbeef"}
    msg = await bus.receive("test")
    assert msg == "covfefe"
    assert bus.queues["test"].qsize() == 0
示例#8
0
async def test_maxsize():
    """
    Test a queue maxsize behaves as expected
    Maxsize=-1 is enabled by default
    """
    bus = MessageBus()
    bus.add_queue("async")
    bus.add_queue("mp", mp=True)
    assert bus.queues["async"].maxsize == -1
    # No maxsize getter on mp queues

    assert bus.queues["async"].empty()
    assert bus.queues["mp"].empty()

    for i in range(1000):
        await bus.send("async", i)
        await bus.send("mp", i)

    assert not bus.queues["async"].full()
    assert not bus.queues["mp"].full()
示例#9
0
async def test_run_async_parallel():
    """
    Test using run to get messages from a queue using an async function executed in parallel
    """
    bus = MessageBus()
    bus.add_queue("input")
    bus.add_queue("end", maxsize=1)
    assert isinstance(bus.queues["input"], asyncio.Queue)
    assert bus.queues["input"].qsize() == 0

    await bus.send("input", 0)
    await bus.send("input", 1)
    await bus.send("input", 2)

    assert bus.queues["input"].qsize() == 3

    done = {0: False, 1: False, 2: False}

    async def torun(count):
        await asyncio.sleep(0)

        if count == 0:
            # Wait for 7 seconds, in the meantime other tasks will be scheduled
            # and executed.
            await asyncio.sleep(7)
        elif count == 1:
            pass
        elif count == 2:
            await bus.send("end", count)
        else:
            assert False

        done[count] = True

    task = asyncio.create_task(bus.run(torun, "input", sequential=False))

    await bus.receive("end") == 1
    task.cancel()
    assert bus.queues["input"].qsize() == 0
    assert bus.queues["end"].qsize() == 0
    assert done == {0: False, 1: True, 2: True}
示例#10
0
async def test_run_async_without_output_queue():
    """
    Test using run to get messages from a queue using an async function and without an output queue
    """
    bus = MessageBus()
    bus.add_queue("input")
    bus.add_queue("end", maxsize=1)
    assert isinstance(bus.queues["input"], asyncio.Queue)
    assert bus.queues["input"].qsize() == 0

    await bus.send("input", "test x")
    await bus.send("input", "hello world.")

    assert bus.queues["input"].qsize() == 2

    count = 0

    async def torun(payload):
        nonlocal count

        if count == 0:
            assert payload == "test x"
        elif count == 1:
            assert payload == "hello world."
            await bus.send("end", count)
        else:
            assert False

        count += 1

    task = asyncio.create_task(bus.run(torun, "input"))

    await bus.receive("end") == 1
    task.cancel()
    assert bus.queues["input"].qsize() == 0
    assert bus.queues["end"].qsize() == 0
示例#11
0
def test_queue_creation():
    """
    Test queues creation with different types
    """
    bus = MessageBus()
    assert len(bus.queues) == 0

    bus.add_queue("test")
    assert len(bus.queues) == 1

    with pytest.raises(AssertionError) as e:
        bus.add_queue("test")
    assert str(e.value) == "Queue test already setup"
    assert len(bus.queues) == 1

    bus.add_queue("another")
    assert len(bus.queues) == 2

    bus.add_queue("different", mp=True)
    assert len(bus.queues) == 3

    assert isinstance(bus.queues["test"], asyncio.Queue)
    assert isinstance(bus.queues["another"], asyncio.Queue)
    assert isinstance(bus.queues["different"], multiprocessing.queues.Queue)
示例#12
0
async def test_crash_utf8_author(PhabricatorMock, mock_mc):
    """
    Run mercurial worker on a single diff
    but the patch author has utf-8 chars in its name
    """
    diff = {
        "revisionPHID": "PHID-DREV-badutf8",
        "baseRevision": "missing",
        "phid": "PHID-DIFF-badutf8",
        "id": 555,
    }
    build = MockBuild(4444, "PHID-REPO-mc", 5555, "PHID-build-badutf8", diff)
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    bus = MessageBus()
    bus.add_queue("phabricator")

    # The patched and config files should not exist at first
    repo_dir = mock_mc.repo.root().decode("utf-8")
    config = os.path.join(repo_dir, "try_task_config.json")
    target = os.path.join(repo_dir, "test.txt")
    assert not os.path.exists(target)
    assert not os.path.exists(config)

    worker = MercurialWorker(
        "mercurial", "phabricator", repositories={"PHID-REPO-mc": mock_mc}
    )
    worker.register(bus)
    assert len(worker.repositories) == 1

    await bus.send("mercurial", build)
    assert bus.queues["mercurial"].qsize() == 1

    # Run the mercurial worker on that patch only
    task = asyncio.create_task(worker.run())
    mode, out_build, details = await bus.receive("phabricator")
    task.cancel()

    # Check we have the patch with utf-8 author properly applied
    assert [(c.author, c.desc) for c in mock_mc.repo.log()] == [
        (
            b"libmozevent <*****@*****.**>",
            b"try_task_config for code-review\n"
            b"Differential Diff: PHID-DIFF-badutf8",
        ),
        (
            b"Andr\xc3\xa9 XXXX <*****@*****.**>",
            b"This patch has an author with utf8 chars\n"
            b"Differential Diff: PHID-DIFF-badutf8",
        ),
        (b"test", b"Readme"),
    ]

    # The phab output should be successful
    assert mode == "success"
    assert out_build == build
    assert details[
        "treeherder_url"
    ] == "https://treeherder.mozilla.org/#/jobs?repo=try&revision={}".format(
        mock_mc.repo.tip().node.decode("utf-8")
    )
    assert details["revision"] == mock_mc.repo.tip().node.decode("utf-8")
示例#13
0
async def test_push_to_try_nss(PhabricatorMock, mock_nss):
    """
    Run mercurial worker on a single diff
    with a push to try server, but with NSS support (try syntax)
    """
    diff = {
        "phid": "PHID-DIFF-test123",
        "revisionPHID": "PHID-DREV-deadbeef",
        "id": 1234,
        # Revision does not exist, will apply on tip
        "baseRevision": "abcdef12345",
    }
    build = MockBuild(1234, "PHID-REPO-nss", 5678, "PHID-HMBT-deadbeef", diff)
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    bus = MessageBus()
    bus.add_queue("phabricator")

    # Get initial tip commit in repo
    initial = mock_nss.repo.tip()

    # The patched and config files should not exist at first
    repo_dir = mock_nss.repo.root().decode("utf-8")
    config = os.path.join(repo_dir, "try_task_config.json")
    target = os.path.join(repo_dir, "test.txt")
    assert not os.path.exists(target)
    assert not os.path.exists(config)

    worker = MercurialWorker(
        "mercurial", "phabricator", repositories={"PHID-REPO-nss": mock_nss}
    )
    worker.register(bus)
    assert len(worker.repositories) == 1

    await bus.send("mercurial", build)
    assert bus.queues["mercurial"].qsize() == 1
    task = asyncio.create_task(worker.run())

    # Check the treeherder link was queued
    mode, out_build, details = await bus.receive("phabricator")
    tip = mock_nss.repo.tip()
    assert mode == "success"
    assert out_build == build
    assert details[
        "treeherder_url"
    ] == "https://treeherder.mozilla.org/#/jobs?repo=try&revision={}".format(
        tip.node.decode("utf-8")
    )
    assert details["revision"] == tip.node.decode("utf-8")
    task.cancel()

    # The target should have content now
    assert os.path.exists(target)
    assert open(target).read() == "First Line\nSecond Line\n"

    # The config should have content now
    assert os.path.exists(config)
    assert json.load(open(config)) == {
        "version": 2,
        "parameters": {
            "code-review": {"phabricator-build-target": "PHID-HMBT-deadbeef"}
        },
    }

    # Get tip commit in repo
    # It should be different from the initial one (patches + config have applied)
    assert tip.node != initial.node

    # Check all commits messages
    assert [c.desc for c in mock_nss.repo.log()] == [
        b"try: -a -b XXX -c YYY",
        b"Bug XXX - A second commit message\nDifferential Diff: PHID-DIFF-test123",
        b"Bug XXX - A first commit message\nDifferential Diff: PHID-DIFF-xxxx",
        b"Readme",
    ]

    # Check the push to try has been called
    # with tip commit
    ssh_conf = 'ssh -o StrictHostKeyChecking="no" -o User="******" -o IdentityFile="{}"'.format(
        mock_nss.ssh_key_path
    )
    mock_nss.repo.push.assert_called_with(
        dest=b"http://nss/try", force=True, rev=tip.node, ssh=ssh_conf.encode("utf-8")
    )
示例#14
0
async def test_dont_push_skippable_files_to_try(PhabricatorMock, mock_mc):
    """
    Run mercurial worker on a single diff
    that skips the push to try server
    """
    bus = MessageBus()
    bus.add_queue("phabricator")

    # Preload the build
    diff = {
        "phid": "PHID-DIFF-test123",
        "revisionPHID": "PHID-DREV-deadbeef",
        "id": 1234,
        # Revision does not exist, will apply on tip
        "baseRevision": "abcdef12345",
    }
    build = MockBuild(1234, "PHID-REPO-mc", 5678, "PHID-HMBT-deadbeef", diff)
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    # Get initial tip commit in repo
    initial = mock_mc.repo.tip()

    # The patched and config files should not exist at first
    repo_dir = mock_mc.repo.root().decode("utf-8")
    config = os.path.join(repo_dir, "try_task_config.json")
    target = os.path.join(repo_dir, "test.txt")
    assert not os.path.exists(target)
    assert not os.path.exists(config)

    worker = MercurialWorker(
        "mercurial",
        "phabricator",
        repositories={"PHID-REPO-mc": mock_mc},
        skippable_files=["test.txt"],
    )
    worker.register(bus)
    assert len(worker.repositories) == 1

    await bus.send("mercurial", build)
    assert bus.queues["mercurial"].qsize() == 1
    task = asyncio.create_task(worker.run())

    # Check the treeherder link was queued
    mode, out_build, details = await bus.receive("phabricator")
    tip = mock_mc.repo.tip()
    assert mode == "fail:ineligible"
    assert out_build == build
    assert (
        details["message"]
        == "Modified files match skippable internal configuration files"
    )
    task.cancel()

    # The target should have content now
    assert os.path.exists(target)
    assert open(target).read() == "First Line\nSecond Line\n"

    # Get tip commit in repo
    # It should be different from the initial one (patches + config have applied)
    assert tip.node != initial.node

    # Check all commits messages
    assert [c.desc for c in mock_mc.repo.log()] == [
        b"Bug XXX - A second commit message\nDifferential Diff: PHID-DIFF-test123",
        b"Bug XXX - A first commit message\nDifferential Diff: PHID-DIFF-xxxx",
        b"Readme",
    ]

    # Check all commits authors
    assert [c.author for c in mock_mc.repo.log()] == [
        b"John Doe <*****@*****.**>",
        b"randomUsername <random>",
        b"test",
    ]

    # Check the push to try has not been called
    mock_mc.repo.push.assert_not_called()
示例#15
0
async def test_push_to_try_existing_rev(PhabricatorMock, mock_mc):
    """
    Run mercurial worker on a single diff
    with a push to try server
    but applying on an existing revision
    """
    bus = MessageBus()
    bus.add_queue("phabricator")
    repo_dir = mock_mc.repo.root().decode("utf-8")

    def _readme(content):
        # Make a commit on README.md in the repo
        readme = os.path.join(repo_dir, "README.md")
        with open(readme, "a") as f:
            f.write(content)
        _, rev = mock_mc.repo.commit(message=content.encode("utf-8"), user=b"test")
        return rev

    # Make two commits, the first one is our base
    base = _readme("Local base for diffs")
    extra = _readme("EXTRA")

    # Preload the build
    diff = {
        "phid": "PHID-DIFF-solo",
        "revisionPHID": "PHID-DREV-solo",
        "id": 9876,
        # Revision does not exist, will apply on tip
        "baseRevision": base,
    }
    build = MockBuild(1234, "PHID-REPO-mc", 5678, "PHID-HMBT-deadbeef", diff)
    build.revision_url = "http://phab.test/D1234"
    with PhabricatorMock as phab:
        phab.load_patches_stack(build)

    # The patched and config files should not exist at first
    target = os.path.join(repo_dir, "solo.txt")
    config = os.path.join(repo_dir, "try_task_config.json")
    assert not os.path.exists(target)
    assert not os.path.exists(config)

    worker = MercurialWorker(
        "mercurial", "phabricator", repositories={"PHID-REPO-mc": mock_mc}
    )
    worker.register(bus)
    assert len(worker.repositories) == 1

    await bus.send("mercurial", build)
    assert bus.queues["mercurial"].qsize() == 1
    task = asyncio.create_task(worker.run())

    # Check the treeherder link was queued
    mode, out_build, details = await bus.receive("phabricator")
    tip = mock_mc.repo.tip()
    assert mode == "success"
    assert out_build == build
    assert details[
        "treeherder_url"
    ] == "https://treeherder.mozilla.org/#/jobs?repo=try&revision={}".format(
        tip.node.decode("utf-8")
    )
    assert details["revision"] == tip.node.decode("utf-8")
    task.cancel()

    # The target should have content now
    assert os.path.exists(target)
    assert open(target).read() == "Solo PATCH\n"

    # Check the try_task_config file
    assert os.path.exists(config)
    assert json.load(open(config)) == {
        "version": 2,
        "parameters": {
            "target_tasks_method": "codereview",
            "optimize_target_tasks": True,
            "phabricator_diff": "PHID-HMBT-deadbeef",
        },
    }

    # Get tip commit in repo
    # It should be different from the initial one (patches and config have applied)
    assert tip.node != base
    assert (
        tip.desc
        == b"""try_task_config for http://phab.test/D1234
Differential Diff: PHID-DIFF-solo"""
    )

    # Check the push to try has been called
    # with tip commit
    ssh_conf = 'ssh -o StrictHostKeyChecking="no" -o User="******" -o IdentityFile="{}"'.format(
        mock_mc.ssh_key_path
    )
    mock_mc.repo.push.assert_called_with(
        dest=b"http://mozilla-central/try",
        force=True,
        rev=tip.node,
        ssh=ssh_conf.encode("utf-8"),
    )

    # Check the parent is the solo patch commit
    parents = mock_mc.repo.parents(tip.node)
    assert len(parents) == 1
    parent = parents[0]
    assert (
        parent.desc
        == b"A nice human readable commit message\nDifferential Diff: PHID-DIFF-solo"
    )

    # Check the grand parent is the base, not extra
    great_parents = mock_mc.repo.parents(parent.node)
    assert len(great_parents) == 1
    great_parent = great_parents[0]
    assert great_parent.node == base

    # Extra commit should not appear
    assert parent.node != extra
    assert great_parent.node != extra
    assert "EXTRA" not in open(os.path.join(repo_dir, "README.md")).read()
示例#16
0
async def test_got_try_task_end(PhabricatorMock, mock_taskcluster):
    bus = MessageBus()
    build = PhabricatorBuild(
        MockRequest(
            diff="125397",
            repo="PHID-REPO-saax4qdxlbbhahhp2kg5",
            revision="36474",
            target="PHID-HMBT-icusvlfibcebizyd33op",
        ))

    payload = {
        "routing": {
            "exchange":
            "exchange/taskcluster-queue/v1/task-completed",
            "key":
            "primary.fUAKaIdkSF6K1NlOgx7-LA.0.aws.i-0a45c84b1709af6a7.gecko-t.t-win10-64.gecko-level-1.RHY-YSgBQ7KlTAaQ5ZWP5g._",
            "other_routes": [
                b"route.tc-treeherder.v2.try.028980a035fb3e214f7645675a01a52234aad0fe.455891"
            ],
        },
        "body": {
            "status": {
                "taskId":
                "W2SMZ3bYTeanBq-WNpUeHA",
                "provisionerId":
                "gecko-t",
                "workerType":
                "t-linux-xlarge",
                "schedulerId":
                "gecko-level-1",
                "taskGroupId":
                "HDnvYOibTMS8h_5Qzv6fWg",
                "deadline":
                "2019-11-23T14:04:41.581Z",
                "expires":
                "2019-12-06T14:04:41.581Z",
                "retriesLeft":
                5,
                "state":
                "completed",
                "runs": [{
                    "runId": 0,
                    "state": "completed",
                    "reasonCreated": "scheduled",
                    "reasonResolved": "completed",
                    "workerGroup": "aws",
                    "workerId": "i-01a6b2a05e2211f7c",
                    "takenUntil": "2019-11-22T15:51:57.083Z",
                    "scheduled": "2019-11-22T15:31:56.661Z",
                    "started": "2019-11-22T15:31:57.162Z",
                    "resolved": "2019-11-22T15:42:46.684Z",
                }],
            },
            "runId": 0,
            "task": {
                "tags": {
                    "kind": "test",
                    "worker-implementation": "docker-worker",
                    "createdForUser": "******",
                    "retrigger": "true",
                    "label": "test-linux64-shippable/opt-awsy-tp6-e10s",
                    "os": "linux",
                }
            },
            "workerGroup": "aws",
            "workerId": "i-01a6b2a05e2211f7c",
            "version": 1,
        },
    }

    taskGroupId = payload["body"]["status"]["taskGroupId"]

    taskcluster_config.secrets["test_selection_enabled"] = True
    taskcluster_config.secrets["test_selection_share"] = 0.1

    with PhabricatorMock as phab:
        bus.add_queue(QUEUE_PHABRICATOR_RESULTS)

        phab.load_patches_stack(build)
        phab.update_state(build)
        phab.load_reviewers(build)

        bugbug_utils = BugbugUtils(phab.api)
        bugbug_utils.register(bus)

    try:
        await bugbug_utils.task_group_to_push.rem(taskGroupId)
    except KeyError:
        pass

    # Nothing happens for tasks that are not test tasks.
    payload["body"]["task"]["tags"]["kind"] = "source-test"
    await bugbug_utils.got_try_task_end(payload)
    assert bus.queues[QUEUE_PHABRICATOR_RESULTS].empty()

    # Nothing happens for tasks that are not registered.
    payload["body"]["task"]["tags"]["kind"] = "test"
    await bugbug_utils.got_try_task_end(payload)
    assert bus.queues[QUEUE_PHABRICATOR_RESULTS].empty()

    push = {
        "build": build,
        "revision": "028980a035fb3e214f7645675a01a52234aad0fe",
    }
    await bugbug_utils.task_group_to_push.set(taskGroupId, push)

    payload["body"]["status"]["state"] = "completed"
    await bugbug_utils.got_try_task_end(payload)
    mode, build_, extras = await bus.receive(QUEUE_PHABRICATOR_RESULTS)
    assert mode == "test_result"
    assert build_ == build
    assert extras == {
        "name": "test-linux64-shippable/opt-awsy-tp6-e10s",
        "result": UnitResultState.Pass,
        "details": None,
    }

    payload["body"]["status"]["state"] = "failed"
    await bugbug_utils.got_try_task_end(payload)
    mode, build_, extras = await bus.receive(QUEUE_PHABRICATOR_RESULTS)
    assert mode == "test_result"
    assert build_ == build
    assert extras == {
        "name":
        "test-linux64-shippable/opt-awsy-tp6-e10s",
        "result":
        UnitResultState.Fail,
        "details":
        "https://treeherder.mozilla.org/#/jobs?repo=try&revision=028980a035fb3e214f7645675a01a52234aad0fe&selectedTaskRun=W2SMZ3bYTeanBq-WNpUeHA-0",
    }
示例#17
0
class Events(object):
    """
    Listen to HTTP notifications from phabricator and trigger new try jobs
    """
    def __init__(self, cache_root):
        # Create message bus shared amongst processes
        self.bus = MessageBus()

        publish = taskcluster_config.secrets["PHABRICATOR"].get(
            "publish", False)

        # Check the redis support is enabled on Heroku
        if heroku.in_dyno():
            assert self.bus.redis_enabled is True, "Need Redis on Heroku"

        community_config = taskcluster_config.secrets.get(
            "taskcluster_community")
        test_selection_enabled = taskcluster_config.secrets.get(
            "test_selection_enabled", False)

        # Run webserver & pulse on web dyno or single instance
        if not heroku.in_dyno() or heroku.in_web_dyno():

            # Create web server
            self.webserver = WebServer(QUEUE_WEB_BUILDS)
            self.webserver.register(self.bus)

            # Create pulse listener
            exchanges = {}
            if taskcluster_config.secrets["autoland_enabled"]:
                logger.info("Autoland ingestion is enabled")
                # autoland ingestion
                exchanges[QUEUE_PULSE_AUTOLAND] = [(PULSE_TASK_GROUP_RESOLVED,
                                                    ["#.gecko-level-3.#"])]

            # Create pulse listeners for bugbug test selection task and unit test failures.
            if community_config is not None and test_selection_enabled:
                exchanges[QUEUE_PULSE_TRY_TASK_END] = [
                    (PULSE_TASK_COMPLETED, ["#.gecko-level-1.#"]),
                    (PULSE_TASK_FAILED, ["#.gecko-level-1.#"]),
                    # https://bugzilla.mozilla.org/show_bug.cgi?id=1599863
                    # (
                    #    "exchange/taskcluster-queue/v1/task-exception",
                    #    ["#.gecko-level-1.#"],
                    # ),
                ]

                self.community_pulse = PulseListener(
                    {
                        QUEUE_PULSE_BUGBUG_TEST_SELECT: [(
                            "exchange/taskcluster-queue/v1/task-completed",
                            ["route.project.relman.bugbug.test_select"],
                        )]
                    },
                    taskcluster_config.secrets["communitytc_pulse_user"],
                    taskcluster_config.secrets["communitytc_pulse_password"],
                    "communitytc",
                )
                # Manually register to set queue as redis
                self.community_pulse.bus = self.bus
                self.bus.add_queue(QUEUE_PULSE_BUGBUG_TEST_SELECT, redis=True)
                self.bus.add_queue(QUEUE_PULSE_TRY_TASK_END, redis=True)
            else:
                self.community_pulse = None

            if exchanges:
                self.pulse = PulseListener(
                    exchanges,
                    taskcluster_config.secrets["pulse_user"],
                    taskcluster_config.secrets["pulse_password"],
                )
                # Manually register to set queue as redis
                self.pulse.bus = self.bus
                self.bus.add_queue(QUEUE_PULSE_AUTOLAND, redis=True)
            else:
                self.pulse = None

        else:
            self.bugbug_utils = None
            self.webserver = None
            self.pulse = None
            self.community_pulse = None
            logger.info("Skipping webserver, bugbug and pulse consumers")

            # Register queues for workers
            self.bus.add_queue(QUEUE_PULSE_AUTOLAND, redis=True)
            self.bus.add_queue(QUEUE_PULSE_BUGBUG_TEST_SELECT, redis=True)
            self.bus.add_queue(QUEUE_PULSE_TRY_TASK_END, redis=True)
            self.bus.add_queue(QUEUE_WEB_BUILDS, redis=True)

        # Run work processes on worker dyno or single instance
        if not heroku.in_dyno() or heroku.in_worker_dyno():
            self.workflow = CodeReview(
                api_key=taskcluster_config.secrets["PHABRICATOR"]["api_key"],
                url=taskcluster_config.secrets["PHABRICATOR"]["url"],
                publish=publish,
                user_blacklist=taskcluster_config.secrets["user_blacklist"],
            )
            self.workflow.register(self.bus)

            # Build mercurial worker and queue
            self.mercurial = MercurialWorker(
                QUEUE_MERCURIAL,
                QUEUE_MERCURIAL_APPLIED,
                repositories=self.workflow.get_repositories(
                    taskcluster_config.secrets["repositories"],
                    cache_root,
                    default_ssh_key=taskcluster_config.secrets["ssh_key"],
                ),
            )
            self.mercurial.register(self.bus)

            # Setup monitoring for newly created tasks
            self.monitoring = Monitoring(
                taskcluster_config,
                QUEUE_MONITORING,
                taskcluster_config.secrets["admins"],
                MONITORING_PERIOD,
            )
            self.monitoring.register(self.bus)

            # Setup monitoring for newly created community tasks
            if community_config is not None:
                self.community_monitoring = Monitoring(
                    community_taskcluster_config,
                    QUEUE_MONITORING_COMMUNITY,
                    taskcluster_config.secrets["admins"],
                    MONITORING_PERIOD,
                )
                self.community_monitoring.register(self.bus)
            else:
                self.community_monitoring = None

            self.bugbug_utils = BugbugUtils(self.workflow.api)
            self.bugbug_utils.register(self.bus)
        else:
            self.workflow = None
            self.mercurial = None
            self.monitoring = None
            self.community_monitoring = None
            self.bugbug_utils = None
            logger.info("Skipping workers consumers")

    def run(self):
        consumers = []

        # Code review main workflow
        if self.workflow:
            consumers += [
                # Process Phabricator build received from webserver
                self.bus.run(self.workflow.process_build,
                             QUEUE_WEB_BUILDS,
                             sequential=False),
                # Publish results on Phabricator
                self.bus.run(
                    self.workflow.publish_results,
                    QUEUE_PHABRICATOR_RESULTS,
                    sequential=False,
                ),
                # Trigger autoland tasks
                self.bus.run(
                    self.workflow.trigger_autoland,
                    QUEUE_PULSE_AUTOLAND,
                    sequential=False,
                ),
                # Send to phabricator results publication for normal processing and to bugbug for further analysis
                self.bus.dispatch(
                    QUEUE_MERCURIAL_APPLIED,
                    [QUEUE_PHABRICATOR_RESULTS, QUEUE_BUGBUG_TRY_PUSH],
                ),
            ]

        if self.bugbug_utils:
            consumers += [
                self.bus.run(self.bugbug_utils.process_build,
                             QUEUE_BUGBUG,
                             sequential=False),
                self.bus.run(
                    self.bugbug_utils.process_push,
                    QUEUE_BUGBUG_TRY_PUSH,
                    sequential=False,
                ),
                self.bus.run(
                    self.bugbug_utils.got_try_task_end,
                    QUEUE_PULSE_TRY_TASK_END,
                    sequential=False,
                ),
                self.bus.run(
                    self.bugbug_utils.got_bugbug_test_select_end,
                    QUEUE_PULSE_BUGBUG_TEST_SELECT,
                    sequential=False,
                ),
            ]

        # Add mercurial task
        if self.mercurial:
            consumers.append(self.mercurial.run())

        # Add monitoring task
        if self.monitoring:
            consumers.append(self.monitoring.run())

        # Add community monitoring task
        if self.community_monitoring:
            consumers.append(self.community_monitoring.run())

        # Add pulse listener for task results.
        if self.pulse:
            consumers.append(self.pulse.run())

        # Add communitytc pulse listener for test selection results.
        if self.community_pulse:
            consumers.append(self.community_pulse.run())

        # Start the web server in its own process
        if self.webserver:
            self.webserver.start()

        if consumers:
            # Run all tasks concurrently
            run_tasks(consumers)
        else:
            # Keep the web server process running
            asyncio.get_event_loop().run_forever()

        # Make sure any pending task is run.
        run_tasks(asyncio.Task.all_tasks())

        # Stop the webserver when other async processes are stopped
        if self.webserver:
            self.webserver.stop()