Ejemplo n.º 1
0
    def find_build_info(self, date, fetch_txt_info=True, max_workers=2):
        """
        Find build info for a nightly build, given a date.

        Returns a :class:`NightlyBuildInfo` instance.
        """
        # getting a valid build for a given date on nightly is tricky.
        # there is multiple possible builds folders for one date,
        # and some of them may be invalid (without binary for example)

        # to save time, we will try multiple build folders at the same
        # time in some threads. The first good one found is returned.
        try:
            build_urls = self._get_urls(date)
            LOG.debug("got build_urls %s" % build_urls)
        except HTTPError as exc:
            raise BuildInfoNotFound(str(exc))
        build_info = None

        valid_builds = []
        while build_urls:
            some = build_urls[:max_workers]
            threads = [
                Thread(target=self._fetch_build_info_from_url,
                       args=(url, i, valid_builds))
                for i, url in enumerate(some)
            ]
            for thread in threads:
                thread.daemon = True
                thread.start()
            for thread in threads:
                while thread.is_alive():
                    thread.join(0.1)
            LOG.debug("got valid_builds %s" % valid_builds)
            if valid_builds:
                infos = sorted(valid_builds, key=lambda b: b[0])[0][1]
                if fetch_txt_info:
                    self._update_build_info_from_txt(infos)

                build_info = NightlyBuildInfo(
                    self.fetch_config,
                    build_url=infos["build_url"],
                    build_date=date,
                    changeset=infos.get("changeset"),
                    repo_url=infos.get("repository"),
                )
                break
            build_urls = build_urls[max_workers:]

        if build_info is None:
            raise BuildInfoNotFound("Unable to find build info for %s" % date)

        return build_info
Ejemplo n.º 2
0
    def _fetch_build_info_from_url(self, url, index, lst):
        """
        Retrieve information from a build folder url.

        Stores in a list the url index and a dict instance with keys
        build_url and build_txt_url if respectively a build file and a
        build info file are found for the url.
        """
        data = {}
        if not url.endswith("/"):
            url += "/"
        for link in url_links(url):
            if "build_url" not in data and self.build_regex.match(link):
                data["build_url"] = url + link
            elif "build_txt_url" not in data and self.build_info_regex.match(
                    link):
                data["build_txt_url"] = url + link
        if data:
            # Check that we found all required data. The URL in build_url is
            # required. build_txt_url is optional.
            if "build_url" not in data:
                raise BuildInfoNotFound(
                    "Failed to find a build file in directory {} that "
                    "matches regex '{}'".format(url, self.build_regex.pattern))

            with self._fetch_lock:
                lst.append((index, data))
Ejemplo n.º 3
0
    def _fetch_build_info_from_url(self, url, index, lst):
        """
        Retrieve information from a build folder url.

        Stores in a list the url index and a dict instance with keys
        build_url and build_txt_url if respectively a build file and a
        build info file are found for the url.
        """
        LOG.debug("Fetching build info from {}".format(url))
        data = {}
        if not url.endswith("/"):
            url += "/"
        links = url_links(url)
        if not self.fetch_config.has_build_info:
            links += url_links(self.fetch_config.get_nightly_info_url(url))
        for link in links:
            name = os.path.basename(link)
            if "build_url" not in data and self.build_regex.match(name):
                data["build_url"] = link
            elif "build_txt_url" not in data and self.build_info_regex.match(
                    name):
                data["build_txt_url"] = link
        if data:
            # Check that we found all required data. The URL in build_url is
            # required. build_txt_url is optional.
            if "build_url" not in data:
                raise BuildInfoNotFound(
                    "Failed to find a build file in directory {} that "
                    "matches regex '{}'".format(url, self.build_regex.pattern))

            with self._fetch_lock:
                lst.append((index, data))
Ejemplo n.º 4
0
    def find_build_info(self, push):
        """
        Find build info for an inbound build, given a Push, a changeset or a
        date/datetime.

        if `push` is not an instance of Push (e.g. it is a date, datetime, or
        string representing the changeset), a query to json pushes will be
        done.

        Return a :class:`InboundBuildInfo` instance.
        """
        if not isinstance(push, Push):
            try:
                push = self.jpushes.push(push)
            except MozRegressionError, exc:
                raise BuildInfoNotFound(str(exc))
Ejemplo n.º 5
0
    def find_build_info(self,
                        changeset,
                        fetch_txt_info=True,
                        check_changeset=False):
        """
        Find build info for an inbound build, given a changeset or a date.

        if `check_changeset` is True, the given changeset might be partial
        (< 40 chars) because it will be verified and updated using json pushes.

        Return a :class:`InboundBuildInfo` instance.
        """
        if is_date_or_datetime(changeset):
            changeset = self.jpushes.revision_for_date(changeset)
            check_changeset = False

        # find a task id
        if check_changeset:
            try:
                changeset = self._check_changeset(changeset)
            except MozRegressionError, exc:
                raise BuildInfoNotFound(str(exc))
Ejemplo n.º 6
0
 def fetch(index):
     if func(index):
         raise BuildInfoNotFound("")
     return index
Ejemplo n.º 7
0
    def find_build_info(self, push):
        """
        Find build info for an integration build, given a Push, a changeset or a
        date/datetime.

        if `push` is not an instance of Push (e.g. it is a date, datetime, or
        string representing the changeset), a query to json pushes will be
        done.

        Return a :class:`IntegrationBuildInfo` instance.
        """
        if not isinstance(push, Push):
            try:
                push = self.jpushes.push(push)
            except MozRegressionError as exc:
                raise BuildInfoNotFound(str(exc))

        changeset = push.changeset

        try:
            # taskcluster builds have two possible root urls: we switched
            # from taskcluster.net -> firefox-ci-tc.services.mozilla.com
            # around November 9. to make things faster, we'll iterate through
            # them based on the one that most likely applies to this push
            possible_tc_root_urls = [TC_ROOT_URL, OLD_TC_ROOT_URL]
            if push.utc_date < TC_ROOT_URL_MIGRATION_FLAG_DATE:
                possible_tc_root_urls.reverse()

            task_id = None
            status = None
            for tc_root_url in possible_tc_root_urls:
                LOG.debug("using taskcluster root url %s" % tc_root_url)
                options = self.fetch_config.tk_options(tc_root_url)
                tc_index = taskcluster.Index(options)
                tc_queue = taskcluster.Queue(options)
                tk_routes = self.fetch_config.tk_routes(push)
                stored_failure = None
                for tk_route in tk_routes:
                    LOG.debug("using taskcluster route %r" % tk_route)
                    try:
                        task_id = tc_index.findTask(tk_route)["taskId"]
                    except TaskclusterFailure as ex:
                        LOG.debug("nothing found via route %r" % tk_route)
                        stored_failure = ex
                        continue
                    if task_id:
                        status = tc_queue.status(task_id)["status"]
                        break
                if status:
                    break
            if not task_id:
                raise stored_failure
        except TaskclusterFailure:
            raise BuildInfoNotFound("Unable to find build info using the"
                                    " taskcluster route %r" %
                                    self.fetch_config.tk_route(push))

        # find a completed run for that task
        run_id, build_date = None, None
        for run in reversed(status["runs"]):
            if run["state"] == "completed":
                run_id = run["runId"]
                build_date = datetime.strptime(run["resolved"],
                                               "%Y-%m-%dT%H:%M:%S.%fZ")
                break

        if run_id is None:
            raise BuildInfoNotFound(
                "Unable to find completed runs for task %s" % task_id)
        artifacts = tc_queue.listArtifacts(task_id, run_id)["artifacts"]

        # look over the artifacts of that run
        build_url = None
        for a in artifacts:
            name = os.path.basename(a["name"])
            if self.build_regex.search(name):
                meth = tc_queue.buildUrl
                if self.fetch_config.tk_needs_auth():
                    meth = tc_queue.buildSignedUrl
                build_url = meth("getArtifact", task_id, run_id, a["name"])
                break
        if build_url is None:
            raise BuildInfoNotFound("unable to find a build url for the"
                                    " changeset %r" % changeset)
        return IntegrationBuildInfo(
            self.fetch_config,
            build_url=build_url,
            build_date=build_date,
            changeset=changeset,
            repo_url=self.jpushes.repo_url,
            task_id=task_id,
        )
Ejemplo n.º 8
0
 def fetch(index):
     # last build info can't be fetched
     if index == 9:
         raise BuildInfoNotFound("")
     return index
Ejemplo n.º 9
0
class InboundInfoFetcher(InfoFetcher):
    def __init__(self, fetch_config):
        InfoFetcher.__init__(self, fetch_config)
        options = fetch_config.tk_options()
        self.index = taskcluster.client.Index(options)
        self.queue = taskcluster.Queue(options)
        self.jpushes = JsonPushes(branch=fetch_config.inbound_branch)

    def _check_changeset(self, changeset):
        # return the full changeset
        return self.jpushes.pushlog_for_change(changeset)['changesets'][-1]

    def find_build_info(self,
                        changeset,
                        fetch_txt_info=True,
                        check_changeset=False):
        """
        Find build info for an inbound build, given a changeset or a date.

        if `check_changeset` is True, the given changeset might be partial
        (< 40 chars) because it will be verified and updated using json pushes.

        Return a :class:`InboundBuildInfo` instance.
        """
        if is_date_or_datetime(changeset):
            changeset = self.jpushes.revision_for_date(changeset)
            check_changeset = False

        # find a task id
        if check_changeset:
            try:
                changeset = self._check_changeset(changeset)
            except MozRegressionError, exc:
                raise BuildInfoNotFound(str(exc))
        tk_route = self.fetch_config.tk_inbound_route(changeset)
        LOG.debug('using taskcluster route %r' % tk_route)
        try:
            task_id = self.index.findTask(tk_route)['taskId']
        except TaskclusterFailure:
            # HACK because of
            # https://bugzilla.mozilla.org/show_bug.cgi?id=1199618
            # and https://bugzilla.mozilla.org/show_bug.cgi?id=1211251
            # TODO remove the if statement once these tasks no longer exists
            # (just raise BuildInfoNotFound)
            err = True
            if self.fetch_config.app_name in ('firefox', 'fennec',
                                              'fennec-2.3'):
                err = False
                try:
                    tk_route = tk_route.replace(changeset, changeset[:12])
                    task_id = self.index.findTask(tk_route)['taskId']
                except TaskclusterFailure:
                    err = True
            if err:
                raise BuildInfoNotFound("Unable to find build info using the"
                                        " taskcluster route %r" % tk_route)

        # find a completed run for that task
        run_id, build_date = None, None
        status = self.queue.status(task_id)['status']
        for run in reversed(status['runs']):
            if run['state'] == 'completed':
                run_id = run['runId']
                build_date = datetime.strptime(run["resolved"],
                                               '%Y-%m-%dT%H:%M:%S.%fZ')
                break

        if run_id is None:
            raise BuildInfoNotFound(
                "Unable to find completed runs for task %s" % task_id)
        artifacts = self.queue.listArtifacts(task_id, run_id)['artifacts']

        # look over the artifacts of that run
        build_url = None
        for a in artifacts:
            name = os.path.basename(a['name'])
            if self.build_regex.search(name):
                meth = self.queue.buildUrl
                if self.fetch_config.tk_needs_auth():
                    meth = self.queue.buildSignedUrl
                build_url = meth('getArtifact', task_id, run_id, a['name'])
                break
        if build_url is None:
            raise BuildInfoNotFound("unable to find a build url for the"
                                    " changeset %r" % changeset)
        return InboundBuildInfo(
            self.fetch_config,
            build_url=build_url,
            build_date=build_date,
            changeset=changeset,
            repo_url=self.jpushes.repo_url(),
            task_id=task_id,
        )
Ejemplo n.º 10
0
class InboundInfoFetcher(InfoFetcher):
    def __init__(self, fetch_config):
        InfoFetcher.__init__(self, fetch_config)
        options = fetch_config.tk_options()
        self.index = taskcluster.client.Index(options)
        self.queue = taskcluster.Queue(options)
        self.jpushes = JsonPushes(branch=fetch_config.inbound_branch)

    def find_build_info(self, push):
        """
        Find build info for an inbound build, given a Push, a changeset or a
        date/datetime.

        if `push` is not an instance of Push (e.g. it is a date, datetime, or
        string representing the changeset), a query to json pushes will be
        done.

        Return a :class:`InboundBuildInfo` instance.
        """
        if not isinstance(push, Push):
            try:
                push = self.jpushes.push(push)
            except MozRegressionError, exc:
                raise BuildInfoNotFound(str(exc))

        changeset = push.changeset

        tk_route = self.fetch_config.tk_inbound_route(push)
        LOG.debug('using taskcluster route %r' % tk_route)
        try:
            task_id = self.index.findTask(tk_route)['taskId']
        except TaskclusterFailure:
            # HACK because of
            # https://bugzilla.mozilla.org/show_bug.cgi?id=1199618
            # and https://bugzilla.mozilla.org/show_bug.cgi?id=1211251
            # TODO remove the if statement once these tasks no longer exists
            # (just raise BuildInfoNotFound)
            err = True
            if self.fetch_config.app_name in ('firefox',
                                              'fennec',
                                              'fennec-2.3') \
                    and push.timestamp < TIMESTAMP_GECKO_V2:
                err = False
                try:
                    old_route = tk_route.replace(changeset, changeset[:12])
                    task_id = self.index.findTask(old_route)['taskId']
                except TaskclusterFailure:
                    err = True
            if err:
                raise BuildInfoNotFound("Unable to find build info using the"
                                        " taskcluster route %r" % tk_route)

        # find a completed run for that task
        run_id, build_date = None, None
        status = self.queue.status(task_id)['status']
        for run in reversed(status['runs']):
            if run['state'] == 'completed':
                run_id = run['runId']
                build_date = datetime.strptime(run["resolved"],
                                               '%Y-%m-%dT%H:%M:%S.%fZ')
                break

        if run_id is None:
            raise BuildInfoNotFound(
                "Unable to find completed runs for task %s" % task_id)
        artifacts = self.queue.listArtifacts(task_id, run_id)['artifacts']

        # look over the artifacts of that run
        build_url = None
        for a in artifacts:
            name = os.path.basename(a['name'])
            if self.build_regex.search(name):
                meth = self.queue.buildUrl
                if self.fetch_config.tk_needs_auth():
                    meth = self.queue.buildSignedUrl
                build_url = meth('getArtifact', task_id, run_id, a['name'])
                break
        if build_url is None:
            raise BuildInfoNotFound("unable to find a build url for the"
                                    " changeset %r" % changeset)
        return InboundBuildInfo(
            self.fetch_config,
            build_url=build_url,
            build_date=build_date,
            changeset=changeset,
            repo_url=self.jpushes.repo_url,
            task_id=task_id,
        )
Ejemplo n.º 11
0
    def find_build_info(self, push):
        """
        Find build info for an inbound build, given a Push, a changeset or a
        date/datetime.

        if `push` is not an instance of Push (e.g. it is a date, datetime, or
        string representing the changeset), a query to json pushes will be
        done.

        Return a :class:`InboundBuildInfo` instance.
        """
        if not isinstance(push, Push):
            try:
                push = self.jpushes.push(push)
            except MozRegressionError as exc:
                raise BuildInfoNotFound(str(exc))

        changeset = push.changeset

        tk_routes = self.fetch_config.tk_inbound_routes(push)
        try:
            task_id = None
            stored_failure = None
            for tk_route in tk_routes:
                LOG.debug('using taskcluster route %r' % tk_route)
                try:
                    task_id = self.index.findTask(tk_route)['taskId']
                except TaskclusterFailure as ex:
                    LOG.debug('nothing found via route %r' % tk_route)
                    stored_failure = ex
                    continue
                if task_id:
                    status = self.queue.status(task_id)['status']
                    break
            if not task_id:
                raise stored_failure
        except TaskclusterFailure:
            raise BuildInfoNotFound("Unable to find build info using the"
                                    " taskcluster route %r" %
                                    self.fetch_config.tk_inbound_route(push))

        # find a completed run for that task
        run_id, build_date = None, None
        for run in reversed(status['runs']):
            if run['state'] == 'completed':
                run_id = run['runId']
                build_date = datetime.strptime(run["resolved"],
                                               '%Y-%m-%dT%H:%M:%S.%fZ')
                break

        if run_id is None:
            raise BuildInfoNotFound(
                "Unable to find completed runs for task %s" % task_id)
        artifacts = self.queue.listArtifacts(task_id, run_id)['artifacts']

        # look over the artifacts of that run
        build_url = None
        for a in artifacts:
            name = os.path.basename(a['name'])
            if self.build_regex.search(name):
                meth = self.queue.buildUrl
                if self.fetch_config.tk_needs_auth():
                    meth = self.queue.buildSignedUrl
                build_url = meth('getArtifact', task_id, run_id, a['name'])
                break
        if build_url is None:
            raise BuildInfoNotFound("unable to find a build url for the"
                                    " changeset %r" % changeset)
        return InboundBuildInfo(
            self.fetch_config,
            build_url=build_url,
            build_date=build_date,
            changeset=changeset,
            repo_url=self.jpushes.repo_url,
            task_id=task_id,
        )