Esempio n. 1
0
async def test_risk_analysis_should_trigger(PhabricatorMock, mock_taskcluster):
    bus = MessageBus()
    build = PhabricatorBuild(
        MockRequest(
            diff="125397",
            repo="PHID-REPO-saax4qdxlbbhahhp2kg5",
            revision="36474",
            target="PHID-HMBT-icusvlfibcebizyd33op",
        ))

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

        # Reviewer of the patch is in the list of risk analysis users.
        taskcluster_config.secrets["risk_analysis_users"] = ["ehsan", "heycam"]

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

        assert bugbug_utils.should_run_risk_analysis(build)

        # Author of the patch is in the list of risk analysis users.
        taskcluster_config.secrets["risk_analysis_users"] = [
            "ehsan", "tnguyen"
        ]

        bugbug_utils = BugbugUtils(phab.api)

        assert bugbug_utils.should_run_risk_analysis(build)
Esempio n. 2
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"
Esempio n. 3
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",
    }
Esempio n. 4
0
async def test_got_bugbug_test_select_end(PhabricatorMock, mock_taskcluster):
    bus = MessageBus()
    build = PhabricatorBuild(
        MockRequest(
            diff="125397",
            repo="PHID-REPO-saax4qdxlbbhahhp2kg5",
            revision="36474",
            target="PHID-HMBT-icusvlfibcebizyd33op",
        ))

    diffId = str(build.diff_id)

    payload = {
        "routing": {
            "exchange":
            "exchange/taskcluster-queue/v1/task-completed",
            "key":
            "primary.OhtlizLqT9ah2jVkUL-yvg.0.community-tc-workers-google.8155538221748661937.proj-relman.compute-large.-.OhtlizLqT9ah2jVkUL-yvg._",
            "other_routes": [
                b"[email protected]",
                b"route.notify.irc-channel.#bugbug.on-failed",
                b"route.index.project.relman.bugbug.test_select.latest",
                f"route.index.project.relman.bugbug.test_select.diff.{diffId}".
                encode(),
                b"route.project.relman.bugbug.test_select",
            ],
        },
        "body": {
            "status": {
                "taskId":
                "bugbug-test-select",
                "provisionerId":
                "proj-relman",
                "workerType":
                "compute-large",
                "schedulerId":
                "-",
                "taskGroupId":
                "HDnvYOibTMS8h_5Qzv6fWg",
                "deadline":
                "2019-11-27T17:03:07.100Z",
                "expires":
                "2019-12-27T15:03:07.100Z",
                "retriesLeft":
                5,
                "state":
                "completed",
                "runs": [{
                    "runId": 0,
                    "state": "completed",
                    "reasonCreated": "scheduled",
                    "reasonResolved": "completed",
                    "workerGroup": "community-tc-workers-google",
                    "workerId": "8155538221748661937",
                    "takenUntil": "2019-11-27T15:25:02.767Z",
                    "scheduled": "2019-11-27T15:03:07.606Z",
                    "started": "2019-11-27T15:05:02.786Z",
                    "resolved": "2019-11-27T15:19:24.809Z",
                }],
            },
            "runId": 0,
            "task": {
                "tags": {}
            },
            "workerGroup": "community-tc-workers-google",
            "workerId": "8155538221748661937",
            "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:
        phab.load_patches_stack(build)
        phab.update_state(build)

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

    try:
        await bugbug_utils.diff_to_push.rem(diffId)
    except KeyError:
        pass

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

    # Nothing happens when diff_to_push is empty.
    with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps:
        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select",
            body=mock_taskcluster("task-bugbug-test-select.json"),
            content_type="application/json",
        )

        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Ffailure_risk",
            body=mock_taskcluster("artifact-bugbug-test-select-failure-risk"),
        )

        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Fselected_tasks",
            body=mock_taskcluster(
                "artifact-bugbug-test-select-selected-tasks"),
        )

        with pytest.raises(KeyError):
            await bugbug_utils.diff_to_push.get(diffId)
        await bugbug_utils.got_bugbug_test_select_end(payload)
        with pytest.raises(KeyError):
            await bugbug_utils.task_group_to_push.get(taskGroupId)

    # Nothing happens when we are on the wrong Phabricator deployment.
    with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps:
        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select",
            body=mock_taskcluster("task-bugbug-test-select.json"),
            content_type="application/json",
        )

        bugbug_utils.phabricator_deployment = "dev"

        await bugbug_utils.diff_to_push.set(diffId, {
            "revision": "123",
            "build": build
        })
        await bugbug_utils.got_bugbug_test_select_end(payload)
        with pytest.raises(KeyError):
            await bugbug_utils.task_group_to_push.get(taskGroupId)
        await bugbug_utils.diff_to_push.rem(diffId)

        bugbug_utils.phabricator_deployment = "prod"

    # Nothing happens when the failure risk is low.
    with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps:
        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select",
            body=mock_taskcluster("task-bugbug-test-select.json"),
            content_type="application/json",
        )

        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Ffailure_risk",
            body=mock_taskcluster(
                "artifact-bugbug-test-select-failure-risk-0"),
        )

        await bugbug_utils.diff_to_push.set(diffId, {
            "revision": "123",
            "build": build
        })
        await bugbug_utils.got_bugbug_test_select_end(payload)
        with pytest.raises(KeyError):
            await bugbug_utils.task_group_to_push.get(taskGroupId)
        # Wait removal of object from diff_to_push to be done.
        tasks = set(asyncio.all_tasks())
        tasks.remove(asyncio.current_task())
        await asyncio.gather(*tasks)
        with pytest.raises(KeyError):
            await bugbug_utils.diff_to_push.get(diffId)

    # Nothing happens when the failure risk is high but there are no selected tasks.
    with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps:
        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select",
            body=mock_taskcluster("task-bugbug-test-select.json"),
            content_type="application/json",
        )

        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Ffailure_risk",
            body=mock_taskcluster("artifact-bugbug-test-select-failure-risk"),
        )

        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Fselected_tasks",
            body=mock_taskcluster(
                "artifact-bugbug-test-select-selected-tasks-none"),
        )

        await bugbug_utils.diff_to_push.set(diffId, {
            "revision": "123",
            "build": build
        })
        await bugbug_utils.got_bugbug_test_select_end(payload)
        with pytest.raises(KeyError):
            await bugbug_utils.task_group_to_push.get(taskGroupId)
        # Wait removal of object from diff_to_push to be done.
        tasks = set(asyncio.all_tasks())
        tasks.remove(asyncio.current_task())
        await asyncio.gather(*tasks)
        with pytest.raises(KeyError):
            await bugbug_utils.diff_to_push.get(diffId)

    # Stuff happens.
    with responses.RequestsMock(assert_all_requests_are_fired=True) as rsps:
        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select",
            body=mock_taskcluster("task-bugbug-test-select.json"),
            content_type="application/json",
        )

        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Ffailure_risk",
            body=mock_taskcluster("artifact-bugbug-test-select-failure-risk"),
        )

        rsps.add(
            responses.GET,
            "http://community_taskcluster.test/api/queue/v1/task/bugbug-test-select/artifacts/public%2Fselected_tasks",
            body=mock_taskcluster(
                "artifact-bugbug-test-select-selected-tasks"),
        )

        rsps.add(
            responses.GET,
            "http://taskcluster.test/api/index/v1/task/gecko.v2.try.revision.123.taskgraph.decision",
            body=mock_taskcluster("index-gecko-decision.json"),
            content_type="application/json",
        )

        rsps.add(
            responses.GET,
            f"http://taskcluster.test/api/queue/v1/task/{taskGroupId}/artifacts/public%2Factions.json",
            body=mock_taskcluster("artifact-gecko-decision-actions.json"),
            content_type="application/json",
        )

        def trigger_hook_callback(request):
            payload = json.loads(request.body)
            assert payload == json.loads(
                mock_taskcluster("trigger-hook-add-new-jobs-post-body.json"))
            return (
                200,
                {
                    "Content-Type": "application/json"
                },
                mock_taskcluster("trigger-hook-add-new-jobs.json"),
            )

        rsps.add_callback(
            responses.POST,
            "http://taskcluster.test/api/hooks/v1/hooks/project-gecko/in-tree-action-1-generic%2Fea5d85cbef/trigger",
            callback=trigger_hook_callback,
        )

        push = {"revision": "123", "build": build}
        await bugbug_utils.diff_to_push.set(diffId, push)
        await bugbug_utils.got_bugbug_test_select_end(payload)
        assert await bugbug_utils.task_group_to_push.get(taskGroupId) == push
        # Wait removal of object from diff_to_push to be done.
        tasks = set(asyncio.all_tasks())
        tasks.remove(asyncio.current_task())
        await asyncio.gather(*tasks)
        with pytest.raises(KeyError):
            await bugbug_utils.diff_to_push.get(diffId)
Esempio n. 5
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()