class TestBuildApiGetMatchingJobs(unittest.TestCase):
    def setUp(self):
        self.query_api = BuildApi()

    def test_matching_jobs_existing(self):
        """_matching_jobs should return the whole dictionary for a buildername in alljobs."""
        self.assertEquals(
            self.query_api.get_matching_jobs("try", "146071751b1e",
                                             'Linux x86-64 try build'),
            json.loads(JOBS_SCHEDULE))

    def test_matching_jobs_invalid(self):
        """_matching_jobs should return an empty list if it receives an invalid buildername."""
        self.assertEquals(
            self.query_api.get_matching_jobs("try", "146071751b1e",
                                             'Invalid buildername'), [])
class TestBuildApiGetMatchingJobs(unittest.TestCase):

    def setUp(self):
        self.query_api = BuildApi()

    def test_matching_jobs_existing(self):
        """_matching_jobs should return the whole dictionary for a buildername in alljobs."""
        self.assertEquals(
            self.query_api.get_matching_jobs(
                "try", "146071751b1e",
                'Linux x86-64 try build'), json.loads(JOBS_SCHEDULE))

    def test_matching_jobs_invalid(self):
        """_matching_jobs should return an empty list if it receives an invalid buildername."""
        self.assertEquals(
            self.query_api.get_matching_jobs(
                "try", "146071751b1e",
                'Invalid buildername'), [])
def query_jobs_buildername(buildername, revision):
    """Return **status** information for a buildername on a given revision."""
    # NOTE: It's unfortunate that there is scheduling and status data.
    #       I think we might need to remove this distinction for the user's
    #       sake.
    status_info = []
    repo_name = query_repo_name_from_buildername(buildername)
    query_api = BuildApi()
    jobs = query_api.get_matching_jobs(repo_name, revision, buildername)
    # The user wants the status data rather than the scheduling data
    for job_schedule_info in jobs:
        status_info.append(_status_info(job_schedule_info))

    return status_info
Example #4
0
def determine_trigger_objective(revision,
                                buildername,
                                trigger_build_if_missing=True):
    """
    Determine if we need to trigger any jobs and which job.

    Returns:

    * The name of the builder we need to trigger
    * Files, if needed, to trigger such builder
    """
    builder_to_trigger = None
    files = None
    repo_name = query_repo_name_from_buildername(buildername)

    build_buildername = determine_upstream_builder(buildername)

    if VALIDATE and not valid_builder(build_buildername):
        raise MozciError("Our platforms mapping system has failed.")

    if build_buildername == buildername:
        # For a build job we know that we don't need files to
        # trigger it and it's the build job we want to trigger
        return build_buildername, None

    # Let's figure out which jobs are associated to such revision
    query_api = BuildApi()
    # Let's only look at jobs that match such build_buildername
    build_jobs = query_api.get_matching_jobs(repo_name, revision,
                                             build_buildername)

    # We need to determine if we need to trigger a build job
    # or the test job
    working_job = None
    running_job = None
    failed_job = None

    LOG.debug("List of matching jobs:")
    for job in build_jobs:
        try:
            status = query_api.get_job_status(job)
        except buildjson.BuildjsonException:
            LOG.debug(
                "We have hit bug 1159279 and have to work around it. We will "
                "pretend that we could not reach the files for it.")
            continue

        # Sometimes running jobs have status unknown in buildapi
        if status in (RUNNING, PENDING, UNKNOWN):
            LOG.debug(
                "We found a running/pending build job. We don't search anymore."
            )
            running_job = job
            # We cannot call _find_files for a running job
            continue

        # Having a coalesced build is the same as not having a build available
        if status == COALESCED:
            LOG.debug(
                "The build we found was a coalesced one; this is the same as "
                "non-existant.")
            continue

        # Successful or failed jobs may have the files we need
        files = _find_files(job)

        if files != [] and _all_urls_reachable(files.values()):
            working_job = job
            break
        else:
            LOG.debug("We can't determine the files for this build or "
                      "can't reach them.")
            files = None

        LOG.info("We found a job that finished but it did not "
                 "produced files. status: %d" % status)
        failed_job = job
    # End of for loop

    if working_job:
        # We found a build job with the necessary files. It could be a
        # successful job, a running job that already emitted files or a
        # testfailed job
        LOG.debug(str(working_job))
        LOG.info("We have the necessary files to trigger the downstream job.")
        # We have the files needed to trigger the test job
        builder_to_trigger = buildername

    elif running_job:
        LOG.info(
            "We found a running/pending build job. We will not trigger another one."
        )
        LOG.info(
            "You have to run the script again after the build job is finished to "
            "trigger %s." % buildername)
        builder_to_trigger = None

    elif failed_job:
        LOG.info(
            "The build job %s failed on revision %s without generating the "
            "necessary files. We will not trigger anything." %
            (build_buildername, revision))
        builder_to_trigger = None

    else:
        # We were trying to build a test job, however, we determined
        # that we need an upstream builder instead
        if not trigger_build_if_missing or not _unique_build_request(
                build_buildername, revision):
            # This is a safeguard to prevent triggering a build
            # job multiple times if it is not intentional
            builder_to_trigger = None
            if not trigger_build_if_missing:
                LOG.info(
                    "We would have to triggered build '%s' in order to trigger "
                    "job '%s'. On this mode we will not trigger either." %
                    (build_buildername, buildername))
        else:
            LOG.info("We will trigger 1) "
                     "'%s' instead of 2) '%s'" %
                     (build_buildername, buildername))
            LOG.info("We need to trigger the build job once (1) "
                     "in order to be able to run the test job (2).")
            if repo_name == 'try':
                LOG.info(
                    "You'll need to run the script again after (1) is done to "
                    "trigger (2).")
            else:
                LOG.info(
                    "After (1) is done and if no coalesccing happens the test "
                    "jobs associated with it will be triggered.")
            builder_to_trigger = build_buildername

    if files:
        return builder_to_trigger, files['packageUrl'], files[
            'testPackagesUrl']
    else:
        return builder_to_trigger, None, None
def determine_trigger_objective(revision, buildername, trigger_build_if_missing=True,
                                will_use_buildapi=False):
    """
    Determine if we need to trigger any jobs and which job.

    Returns:

    * The name of the builder we need to trigger
    * Files, if needed, to trigger such builder
    """
    builder_to_trigger = None
    files = None
    repo_name = query_repo_name_from_buildername(buildername)

    build_buildername = determine_upstream_builder(buildername)

    if VALIDATE and not valid_builder(build_buildername):
        raise MozciError("Our platforms mapping system has failed.")

    if build_buildername == buildername:
        # For a build job we know that we don't need files to
        # trigger it and it's the build job we want to trigger
        return build_buildername, None, None

    # Let's figure out which jobs are associated to such revision
    query_api = BuildApi()
    # Let's only look at jobs that match such build_buildername
    build_jobs = query_api.get_matching_jobs(repo_name, revision, build_buildername)

    # We need to determine if we need to trigger a build job
    # or the test job
    working_job = None
    running_job = None
    failed_job = None

    LOG.debug("List of matching jobs:")
    for job in build_jobs:
        try:
            status = query_api.get_job_status(job)
        except buildjson.BuildjsonException:
            LOG.debug("We have hit bug 1159279 and have to work around it. We will "
                      "pretend that we could not reach the files for it.")
            continue

        # Sometimes running jobs have status unknown in buildapi
        if status in (RUNNING, PENDING, UNKNOWN):
            LOG.debug("We found a running/pending build job. We don't search anymore.")
            running_job = job
            # We cannot call _find_files for a running job
            continue

        # Having a coalesced build is the same as not having a build available
        if status == COALESCED:
            LOG.debug("The build we found was a coalesced one; this is the same as "
                      "non-existant.")
            continue

        # Successful or failed jobs may have the files we need
        files = _find_files(job)

        if files != [] and _all_urls_reachable(files.values()):
            working_job = job
            break
        else:
            LOG.debug("We can't determine the files for this build or "
                      "can't reach them.")
            files = None

        LOG.info("We found a job that finished but it did not "
                 "produced files. status: %d" % status)
        failed_job = job
    # End of for loop

    if working_job:
        # We found a build job with the necessary files. It could be a
        # successful job, a running job that already emitted files or a
        # testfailed job
        LOG.debug(str(working_job))
        LOG.info("We have the necessary files to trigger the downstream job.")
        # We have the files needed to trigger the test job
        builder_to_trigger = buildername

    elif running_job:
        LOG.info("We found a running/pending build job. We will not trigger another one.")
        LOG.info("You have to run the script again after the build job is finished to "
                 "trigger %s." % buildername)
        builder_to_trigger = None

    elif failed_job:
        LOG.info("The build job %s failed on revision %s without generating the "
                 "necessary files. We will not trigger anything." %
                 (build_buildername, revision))
        builder_to_trigger = None

    else:
        # We were trying to build a test job, however, we determined
        # that we need an upstream builder instead
        if not trigger_build_if_missing or not _unique_build_request(build_buildername, revision):
            # This is a safeguard to prevent triggering a build
            # job multiple times if it is not intentional
            builder_to_trigger = None
            if not trigger_build_if_missing:
                LOG.info("We would have to triggered build '%s' in order to trigger "
                         "job '%s'. On this mode we will not trigger either." %
                         (build_buildername, buildername))
        else:
            if will_use_buildapi:
                LOG.info("We will trigger 1) '%s'" % build_buildername)
                LOG.info("instead of 2) '%s'" % buildername)
                LOG.info("We need to trigger the build job once (1) "
                         "in order to be able to run the test job (2).")
                if repo_name == 'try':
                    LOG.info("You'll need to run the script again after (1) is done to "
                             "trigger (2).")
                else:
                    LOG.info("After (1) is done and if no coalesccing happens the test "
                             "jobs associated with it will be triggered.")
            builder_to_trigger = build_buildername

    if files:
        return builder_to_trigger, files['packageUrl'], files['testsUrl']
    else:
        return builder_to_trigger, None, None