Esempio n. 1
0
def dist_git_upload_completed():
    """
    Mark BuildChroot in a Build as uploaded, which means:
        - set it to pending state
        - set BuildChroot.git_hash
        - if it's the last BuildChroot in a Build:
            - delete local srpm
    BuildChroot is identified with task_id which is build id + git branch name
        - For example: 56-f22 -> build 55, chroots fedora-22-*
    """
    result = {"updated": False}

    if "task_id" in flask.request.json:
        app.logger.debug(flask.request.data)
        task_id = flask.request.json["task_id"]
        build_chroots = BuildsLogic.get_chroots_from_dist_git_task_id(task_id)
        build = build_chroots[0].build

        # Is it OK?
        if "git_hash" in flask.request.json and "repo_name" in flask.request.json:
            git_hash = flask.request.json["git_hash"]
            pkg_name = flask.request.json["pkg_name"]
            pkg_version = flask.request.json["pkg_version"]

            # Now I need to assign a package to this build
            package = PackagesLogic.get(build.copr.id, pkg_name).first()
            if not package:
                package = PackagesLogic.add(build.copr.owner, build.copr,
                                            pkg_name)
                db.session.add(package)
                db.session.flush()

            build.package_id = package.id
            build.pkg_version = pkg_version

            for ch in build_chroots:
                ch.status = helpers.StatusEnum("pending")
                ch.git_hash = git_hash

        # Failed?
        elif "error" in flask.request.json:
            error_type = flask.request.json["error"]

            try:
                build.fail_type = helpers.FailTypeEnum(error_type)
            except KeyError:
                build.fail_type = helpers.FailTypeEnum("unknown_error")

            for ch in build_chroots:
                ch.status = helpers.StatusEnum("failed")

        # is it the last chroot?
        if not build.has_importing_chroot:
            BuildsLogic.delete_local_srpm(build)

        db.session.commit()

        result.update({"updated": True})

    return flask.jsonify(result)
Esempio n. 2
0
 def fail_type_text(self):
     return helpers.FailTypeEnum(self.fail_type)
Esempio n. 3
0
class Build(db.Model, helpers.Serializer):
    """
    Representation of one build in one copr
    """
    __table_args__ = (db.Index('build_canceled', "canceled"), )

    id = db.Column(db.Integer, primary_key=True)
    # single url to the source rpm, should not contain " ", "\n", "\t"
    pkgs = db.Column(db.Text)
    # built packages
    built_packages = db.Column(db.Text)
    # version of the srpm package got by rpm
    pkg_version = db.Column(db.Text)
    # was this build canceled by user?
    canceled = db.Column(db.Boolean, default=False)
    # list of space separated additional repos
    repos = db.Column(db.Text)
    # the three below represent time of important events for this build
    # as returned by int(time.time())
    submitted_on = db.Column(db.Integer, nullable=False)
    # url of the build results
    results = db.Column(db.Text)
    # memory requirements for backend builder
    memory_reqs = db.Column(db.Integer, default=constants.DEFAULT_BUILD_MEMORY)
    # maximum allowed time of build, build will fail if exceeded
    timeout = db.Column(db.Integer, default=constants.DEFAULT_BUILD_TIMEOUT)
    # enable networking during a build process
    enable_net = db.Column(db.Boolean,
                           default=False,
                           server_default="0",
                           nullable=False)
    # Source of the build: type identifier
    source_type = db.Column(db.Integer,
                            default=helpers.BuildSourceEnum("unset"))
    # Source of the build: description in json, example: git link, srpm url, etc.
    source_json = db.Column(db.Text)
    # Type of failure: type identifier
    fail_type = db.Column(db.Integer, default=helpers.FailTypeEnum("unset"))
    # background builds has lesser priority than regular builds.
    is_background = db.Column(db.Boolean,
                              default=False,
                              server_default="0",
                              nullable=False)

    # relations
    user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
    user = db.relationship("User", backref=db.backref("builds"))
    copr_id = db.Column(db.Integer, db.ForeignKey("copr.id"))
    copr = db.relationship("Copr", backref=db.backref("builds"))
    package_id = db.Column(db.Integer, db.ForeignKey("package.id"))
    package = db.relationship("Package")

    chroots = association_proxy("build_chroots", "mock_chroot")

    @property
    def user_name(self):
        return self.user.name

    @property
    def group_name(self):
        return self.copr.group.name

    @property
    def copr_name(self):
        return self.copr.name

    @property
    def fail_type_text(self):
        return helpers.FailTypeEnum(self.fail_type)

    @property
    def is_older_results_naming_used(self):
        # we have changed result directory naming together with transition to dist-git
        # that's why we use so strange criterion
        return self.build_chroots[0].git_hash is None

    @property
    def repos_list(self):
        if self.repos is None:
            return list()
        else:
            return self.repos.split()

    @property
    def result_dir_name(self):
        # We can remove this ugly condition after migrating Copr to new machines
        # It is throw-back from era before dist-git
        if self.is_older_results_naming_used:
            return self.src_pkg_name

        return "{:08d}-{}".format(self.id, self.package.name)

    @property
    def source_json_dict(self):
        if not self.source_json:
            return {}
        return json.loads(self.source_json)

    @property
    def started_on(self):
        return self.min_started_on

    @property
    def min_started_on(self):
        mb_list = [
            chroot.started_on for chroot in self.build_chroots
            if chroot.started_on
        ]
        if len(mb_list) > 0:
            return min(mb_list)
        else:
            return None

    @property
    def ended_on(self):
        return self.max_ended_on

    @property
    def max_ended_on(self):
        if not self.build_chroots:
            return None
        if any(chroot.ended_on is None for chroot in self.build_chroots):
            return None
        return max(chroot.ended_on for chroot in self.build_chroots)

    @property
    def chroots_started_on(self):
        return {
            chroot.name: chroot.started_on
            for chroot in self.build_chroots
        }

    @property
    def chroots_ended_on(self):
        return {chroot.name: chroot.ended_on for chroot in self.build_chroots}

    @property
    def source_type_text(self):
        return helpers.BuildSourceEnum(self.source_type)

    @property
    def source_metadata(self):
        if self.source_json is None:
            return None

        try:
            return json.loads(self.source_json)
        except (TypeError, ValueError):
            return None

    @property
    def chroot_states(self):
        return map(lambda chroot: chroot.status, self.build_chroots)

    def get_chroots_by_status(self, statuses=None):
        """
        Get build chroots with states which present in `states` list
        If states == None, function returns build_chroots
        """
        chroot_states_map = dict(zip(self.build_chroots, self.chroot_states))
        if statuses is not None:
            statuses = set(statuses)
        else:
            return self.build_chroots

        return [
            chroot for chroot, status in chroot_states_map.items()
            if status in statuses
        ]

    @property
    def chroots_dict_by_name(self):
        return {b.name: b for b in self.build_chroots}

    @property
    def has_pending_chroot(self):
        # FIXME bad name
        # used when checking if the repo is initialized and results can be set
        # i think this is the only purpose - check
        return StatusEnum("pending") in self.chroot_states or \
            StatusEnum("starting") in self.chroot_states

    @property
    def has_unfinished_chroot(self):
        return StatusEnum("pending") in self.chroot_states or \
            StatusEnum("starting") in self.chroot_states or \
            StatusEnum("running") in self.chroot_states

    @property
    def has_importing_chroot(self):
        return StatusEnum("importing") in self.chroot_states

    @property
    def status(self):
        """
        Return build status according to build status of its chroots
        """
        if self.canceled:
            return StatusEnum("canceled")

        for state in [
                "running", "starting", "importing", "pending", "failed",
                "succeeded", "skipped"
        ]:
            if StatusEnum(state) in self.chroot_states:
                return StatusEnum(state)

    @property
    def state(self):
        """
        Return text representation of status of this build
        """

        if self.status is not None:
            return StatusEnum(self.status)

        return "unknown"

    @property
    def cancelable(self):
        """
        Find out if this build is cancelable.

        Build is cancelabel only when it's pending (not started)
        """

        return self.status == StatusEnum("pending") or \
            self.status == StatusEnum("importing")

    @property
    def repeatable(self):
        """
        Find out if this build is repeatable.

        Build is repeatable only if it's not pending, starting or running
        """
        return self.status not in [
            StatusEnum("pending"),
            StatusEnum("starting"),
            StatusEnum("running"),
        ]

    @property
    def finished(self):
        """
        Find out if this build is in finished state.

        Build is finished only if all its build_chroots are in finished state.
        """
        return all([(chroot.state
                     in ["succeeded", "canceled", "skipped", "failed"])
                    for chroot in self.build_chroots])

    @property
    def persistent(self):
        """
        Find out if this build is persistent.

        This property is inherited from the project.
        """
        return self.copr.persistent

    @property
    def src_pkg_name(self):
        """
        Extract source package name from source name or url
        todo: obsolete
        """
        try:
            src_rpm_name = self.pkgs.split("/")[-1]
        except:
            return None
        if src_rpm_name.endswith(".src.rpm"):
            return src_rpm_name[:-8]
        else:
            return src_rpm_name

    @property
    def package_name(self):
        try:
            return self.package.name
        except:
            return None

    def to_dict(self, options=None, with_chroot_states=False):
        result = super(Build, self).to_dict(options)
        result["src_pkg"] = result["pkgs"]
        del result["pkgs"]
        del result["copr_id"]

        result['source_type'] = helpers.BuildSourceEnum(result['source_type'])
        result["state"] = self.state

        if with_chroot_states:
            result["chroots"] = {b.name: b.state for b in self.build_chroots}

        return result