def test_koji_build_failed(github_pr_event):
    flexmock(AddPullRequestDbTrigger).should_receive("db_trigger").and_return(
        flexmock(job_config_trigger_type=JobConfigTriggerType.release))
    helper = build_helper(
        event=github_pr_event,
        metadata=JobMetadataConfig(targets=["bright-future"]),
    )
    flexmock(koji_build).should_receive("get_all_koji_targets").and_return(
        ["dark-past", "bright-future"]).once()

    flexmock(StatusReporter).should_receive("set_status").with_args(
        state=CommitStatus.pending,
        description="Building SRPM ...",
        check_name="packit-stg/production-build-bright-future",
        url="",
    ).and_return()

    srpm_build_url = get_srpm_log_url_from_flask(2)
    flexmock(StatusReporter).should_receive("set_status").with_args(
        state=CommitStatus.error,
        description="Submit of the build failed: some error",
        check_name="packit-stg/production-build-bright-future",
        url=srpm_build_url,
    ).and_return()

    flexmock(GitProject).should_receive(
        "set_commit_status").and_return().never()
    flexmock(SRPMBuildModel).should_receive("create").and_return(
        SRPMBuildModel(id=2, success=True))
    flexmock(KojiBuildModel).should_receive("get_or_create").and_return(
        KojiBuildModel(id=1))
    flexmock(PackitAPI).should_receive("create_srpm").and_return("my.srpm")

    # koji build
    flexmock(sentry_integration).should_receive(
        "send_to_sentry").and_return().once()
    flexmock(Upstream).should_receive("koji_build").and_raise(
        Exception, "some error")

    result = helper.run_koji_build()
    assert not result["success"]
    assert result["details"]["errors"]
    assert result["details"]["errors"]["bright-future"] == "some error"
def test_koji_build_failed_kerberos(github_pr_event):
    flexmock(AddPullRequestDbTrigger).should_receive("db_trigger").and_return(
        flexmock(job_config_trigger_type=JobConfigTriggerType.release))
    helper = build_helper(
        event=github_pr_event,
        metadata=JobMetadataConfig(targets=["bright-future"]),
    )
    flexmock(koji_build).should_receive("get_all_koji_targets").and_return(
        ["dark-past", "bright-future"]).never()

    flexmock(StatusReporter).should_receive("set_status").with_args(
        state=CommitStatus.pending,
        description="Building SRPM ...",
        check_name="packit-stg/production-build-bright-future",
        url="",
    ).and_return()
    flexmock(StatusReporter).should_receive("set_status").with_args(
        state=CommitStatus.error,
        description=
        "Kerberos authentication error: the bad authentication error",
        check_name="packit-stg/production-build-bright-future",
        url=get_srpm_log_url_from_flask(1),
    ).and_return()

    flexmock(GitProject).should_receive(
        "set_commit_status").and_return().never()
    flexmock(SRPMBuildModel).should_receive("create").and_return(
        SRPMBuildModel(id=1, success=True))
    flexmock(KojiBuildModel).should_receive("get_or_create").and_return(
        KojiBuildModel(id=1))
    flexmock(PackitAPI).should_receive("create_srpm").and_return("my.srpm")

    flexmock(PackitAPI).should_receive("init_kerberos_ticket").and_raise(
        PackitCommandFailedError,
        "Command failed",
        stdout_output="",
        stderr_output="the bad authentication error",
    )

    response = helper.run_koji_build()
    assert not response["success"]
    assert ("Kerberos authentication error: the bad authentication error" ==
            response["details"]["msg"])
Example #3
0
def test_copr_and_koji_build_for_one_trigger(clean_before_and_after):
    pr1 = PullRequestModel.get_or_create(
        pr_id=1,
        namespace="the-namespace",
        repo_name="the-repo-name",
        project_url="https://github.com/the-namespace/the-repo-name",
    )
    pr1_trigger = JobTriggerModel.get_or_create(
        type=JobTriggerModelType.pull_request, trigger_id=pr1.id)
    srpm_build = SRPMBuildModel.create("asd\nqwe\n",
                                       success=True,
                                       trigger_model=pr1)
    copr_build = CoprBuildModel.get_or_create(
        build_id="123456",
        commit_sha="687abc76d67d",
        project_name="SomeUser-hello-world-9",
        owner="packit",
        web_url="https://copr.something.somewhere/123456",
        target=SampleValues.target,
        status="pending",
        srpm_build=srpm_build,
        trigger_model=pr1,
    )
    koji_build = KojiBuildModel.get_or_create(
        build_id="987654",
        commit_sha="687abc76d67d",
        web_url="https://copr.something.somewhere/123456",
        target=SampleValues.target,
        status="pending",
        srpm_build=srpm_build,
        trigger_model=pr1,
    )

    assert copr_build in pr1_trigger.copr_builds
    assert koji_build in pr1_trigger.koji_builds
    assert srpm_build in pr1_trigger.srpm_builds

    assert srpm_build.job_trigger.get_trigger_object() == pr1
    assert copr_build.job_trigger.get_trigger_object() == pr1
    assert koji_build.job_trigger.get_trigger_object() == pr1
def test_koji_build_target_not_supported(github_pr_event):
    trigger = flexmock(job_config_trigger_type=JobConfigTriggerType.release, id=123)
    flexmock(AddPullRequestDbTrigger).should_receive("db_trigger").and_return(trigger)
    helper = build_helper(
        event=github_pr_event,
        metadata=JobMetadataConfig(targets=["nonexisting-target"]),
        db_trigger=trigger,
    )
    flexmock(koji_build).should_receive("get_all_koji_targets").and_return(
        ["dark-past", "bright-future"]
    ).once()

    flexmock(StatusReporter).should_receive("set_status").with_args(
        state=CommitStatus.pending,
        description="Building SRPM ...",
        check_name="packit-stg/production-build-nonexisting-target",
        url="",
    ).and_return()
    flexmock(StatusReporter).should_receive("set_status").with_args(
        state=CommitStatus.error,
        description="Target not supported: nonexisting-target",
        check_name="packit-stg/production-build-nonexisting-target",
        url=get_srpm_log_url_from_flask(1),
    ).and_return()

    flexmock(GitProject).should_receive("set_commit_status").and_return().never()
    flexmock(SRPMBuildModel).should_receive("create").and_return(
        SRPMBuildModel(id=1, success=True)
    )
    flexmock(KojiBuildModel).should_receive("get_or_create").and_return(
        KojiBuildModel(id=1)
    )
    flexmock(PackitAPI).should_receive("create_srpm").and_return("my.srpm")

    response = helper.run_koji_build()
    assert not response["success"]
    assert (
        "Target not supported: nonexisting-target"
        == response["details"]["errors"]["nonexisting-target"]
    )
Example #5
0
    def get(self, id):
        """A specific koji build details. From koji_build hash, filled by worker."""
        builds_list = KojiBuildModel.get_all_by_build_id(str(id))

        if not builds_list.first():
            return response_maker(
                {"error": "No info about build stored in DB"},
                status=HTTPStatus.NOT_FOUND.value,
            )

        build = builds_list[0]

        build_dict = {
            "build_id": build.build_id,
            "status": build.status,
            "build_start_time": optional_time(build.build_start_time),
            "build_finished_time": optional_time(build.build_finished_time),
            "build_submitted_time": optional_time(build.build_submitted_time),
            "chroot": build.target,
            "web_url": build.web_url,
            # from old data, sometimes build_logs_url is same and sometimes different to web_url
            "build_logs_url": build.build_logs_url,
            "pr_id": build.get_pr_id(),
            "branch_name": build.get_branch_name(),
            "ref": build.commit_sha,
            "release": build.get_release_tag(),
        }

        project = build.get_project()
        if project:
            build_dict["project_url"] = project.project_url
            build_dict["repo_namespace"] = project.namespace
            build_dict["repo_name"] = project.repo_name

        build_dict[
            "srpm_logs"] = build.srpm_build.logs if build.srpm_build else None

        return response_maker(build_dict)
Example #6
0
 def build_model(self) -> Optional[KojiBuildModel]:
     if not self._build_model:
         self._build_model = KojiBuildModel.get_by_build_id(
             build_id=self.build_id)
     return self._build_model
Example #7
0
def koji_build_info(id_):
    log_service_versions()
    build = KojiBuildModel.get_by_id(id_)
    if build:
        return _get_build_info(build, build_description="Koji build")
    return f"We can't find any info about Koji build {id_}.\n", 404
Example #8
0
 def build_model(self) -> Optional[KojiBuildModel]:
     if not self._build_model_searched and not self._build_model:
         self._build_model = KojiBuildModel.get_by_build_id(
             build_id=self.build_id)
         self._build_model_searched = True
     return self._build_model
    def run(self):
        build = KojiBuildModel.get_by_build_id(
            build_id=str(self.koji_event.build_id))

        if not build:
            msg = f"Koji build {self.koji_event.build_id} not found in the database."
            logger.warning(msg)
            return TaskResults(success=False, details={"msg": msg})

        logger.debug(
            f"Build on {build.target} in koji changed state "
            f"from {self.koji_event.old_state} to {self.koji_event.state}.")

        build.set_build_start_time(
            datetime.utcfromtimestamp(self.koji_event.start_time) if self.
            koji_event.start_time else None)

        build.set_build_finished_time(
            datetime.utcfromtimestamp(self.koji_event.completion_time) if self.
            koji_event.completion_time else None)

        url = get_koji_build_info_url_from_flask(build.id)
        build_job_helper = KojiBuildJobHelper(
            service_config=self.service_config,
            package_config=self.package_config,
            project=self.project,
            metadata=self.data,
            db_trigger=self.db_trigger,
            job_config=self.job_config,
        )

        if self.koji_event.state == KojiBuildState.open:
            build.set_status("pending")
            build_job_helper.report_status_to_all_for_chroot(
                description="RPM build is in progress...",
                state=CommitStatus.pending,
                url=url,
                chroot=build.target,
            )
        elif self.koji_event.state == KojiBuildState.closed:
            build.set_status("success")
            build_job_helper.report_status_to_all_for_chroot(
                description="RPMs were built successfully.",
                state=CommitStatus.success,
                url=url,
                chroot=build.target,
            )
        elif self.koji_event.state == KojiBuildState.failed:
            build.set_status("failed")
            build_job_helper.report_status_to_all_for_chroot(
                description="RPMs failed to be built.",
                state=CommitStatus.failure,
                url=url,
                chroot=build.target,
            )
        elif self.koji_event.state == KojiBuildState.canceled:
            build.set_status("error")
            build_job_helper.report_status_to_all_for_chroot(
                description="RPMs build was canceled.",
                state=CommitStatus.error,
                url=url,
                chroot=build.target,
            )
        else:
            logger.debug(
                f"We don't react to this koji build state change: {self.koji_event.state}"
            )

        koji_build_logs = self.koji_event.get_koji_build_logs_url()
        build.set_build_logs_url(koji_build_logs)
        koji_rpm_task_web_url = self.koji_event.get_koji_build_logs_url()
        build.set_web_url(koji_rpm_task_web_url)

        msg = (f"Build on {build.target} in koji changed state "
               f"from {self.koji_event.old_state} to {self.koji_event.state}.")
        return TaskResults(success=True, details={"msg": msg})
Example #10
0
 def build(self) -> Optional[KojiBuildModel]:
     if not self._build:
         self._build = KojiBuildModel.get_by_build_id(
             build_id=str(self.koji_event.build_id))
     return self._build
Example #11
0
    def run_koji_build(self) -> TaskResults:
        if not self.is_scratch:
            msg = "Non-scratch builds not possible from upstream."
            self.report_status_to_all(
                description=msg,
                state=CommitStatus.error,
                url=KOJI_PRODUCTION_BUILDS_ISSUE,
            )
            return TaskResults(success=True, details={"msg": msg})

        self.report_status_to_all(
            description="Building SRPM ...", state=CommitStatus.pending
        )
        self.create_srpm_if_needed()

        if not self.srpm_model.success:
            msg = "SRPM build failed, check the logs for details."
            self.report_status_to_all(
                state=CommitStatus.failure,
                description=msg,
                url=get_srpm_log_url_from_flask(self.srpm_model.id),
            )
            return TaskResults(success=False, details={"msg": msg})

        try:
            # We need to do it manually
            # because we don't use PackitAPI.build, but PackitAPI.up.koji_build
            self.api.init_kerberos_ticket()
        except PackitCommandFailedError as ex:
            msg = f"Kerberos authentication error: {ex.stderr_output}"
            logger.error(msg)
            self.report_status_to_all(
                state=CommitStatus.error,
                description=msg,
                url=get_srpm_log_url_from_flask(self.srpm_model.id),
            )
            return TaskResults(success=False, details={"msg": msg})

        errors: Dict[str, str] = {}
        for target in self.build_targets:

            if target not in self.supported_koji_targets:
                msg = f"Target not supported: {target}"
                self.report_status_to_all_for_chroot(
                    state=CommitStatus.error,
                    description=msg,
                    url=get_srpm_log_url_from_flask(self.srpm_model.id),
                    chroot=target,
                )
                errors[target] = msg
                continue

            try:
                build_id, web_url = self.run_build(target=target)
            except Exception as ex:
                sentry_integration.send_to_sentry(ex)
                # TODO: Where can we show more info about failure?
                # TODO: Retry
                self.report_status_to_all_for_chroot(
                    state=CommitStatus.error,
                    description=f"Submit of the build failed: {ex}",
                    url=get_srpm_log_url_from_flask(self.srpm_model.id),
                    chroot=target,
                )
                errors[target] = str(ex)
                continue

            koji_build = KojiBuildModel.get_or_create(
                build_id=str(build_id),
                commit_sha=self.metadata.commit_sha,
                web_url=web_url,
                target=target,
                status="pending",
                srpm_build=self.srpm_model,
                trigger_model=self.db_trigger,
            )
            url = get_koji_build_info_url_from_flask(id_=koji_build.id)
            self.report_status_to_all_for_chroot(
                state=CommitStatus.pending,
                description="Building RPM ...",
                url=url,
                chroot=target,
            )

        if errors:
            return TaskResults(
                success=False,
                details={
                    "msg": "Koji build submit was not successful for all chroots.",
                    "errors": errors,
                },
            )

        # TODO: release the hounds!
        """
        celery_app.send_task(
            "task.babysit_koji_build",
            args=(build_metadata.build_id,),
            countdown=120,  # do the first check in 120s
        )
        """

        return TaskResults(success=True, details={})
Example #12
0
class KojiBuildJobHelper(BaseBuildJobHelper):
    job_type_build = JobType.production_build
    job_type_test = None
    status_name_build: str = "production-build"
    status_name_test: str = None

    def __init__(
        self,
        service_config: ServiceConfig,
        package_config: PackageConfig,
        project: GitProject,
        metadata: EventData,
        db_trigger,
        job_config: JobConfig,
        targets_override: Optional[Set[str]] = None,
    ):
        super().__init__(
            service_config=service_config,
            package_config=package_config,
            project=project,
            metadata=metadata,
            db_trigger=db_trigger,
            job_config=job_config,
            targets_override=targets_override,
        )
        self.msg_retrigger: str = MSG_RETRIGGER.format(
            job="build", command="production-build", place="pull request"
        )

        # Lazy properties
        self._supported_koji_targets = None

    @property
    def is_scratch(self) -> bool:
        return self.job_build and self.job_build.metadata.scratch

    @property
    def build_targets_all(self) -> Set[str]:
        """
        Return all valid Koji targets/chroots from config.
        """
        return get_koji_targets(*self.configured_build_targets)

    @property
    def tests_targets_all(self) -> Set[str]:
        """
        [not used now]
        Return all valid test targets/chroots from config.
        """
        return get_koji_targets(*self.configured_tests_targets)

    @property
    def supported_koji_targets(self):
        if self._supported_koji_targets is None:
            self._supported_koji_targets = get_all_koji_targets()
        return self._supported_koji_targets

    def run_koji_build(self) -> TaskResults:
        self.report_status_to_all(
            description="Building SRPM ...", state=BaseCommitStatus.running
        )
        if results := self.create_srpm_if_needed():
            return results

        if self.srpm_model.status != PG_BUILD_STATUS_SUCCESS:
            msg = "SRPM build failed, check the logs for details."
            self.report_status_to_all(
                state=BaseCommitStatus.failure,
                description=msg,
                url=get_srpm_build_info_url(self.srpm_model.id),
            )
            return TaskResults(success=False, details={"msg": msg})

        try:
            # We need to do it manually
            # because we don't use PackitAPI.build, but PackitAPI.up.koji_build
            self.api.init_kerberos_ticket()
        except PackitCommandFailedError as ex:
            msg = f"Kerberos authentication error: {ex.stderr_output}"
            logger.error(msg)
            self.report_status_to_all(
                state=BaseCommitStatus.error,
                description=msg,
                url=get_srpm_build_info_url(self.srpm_model.id),
            )
            return TaskResults(success=False, details={"msg": msg})

        errors: Dict[str, str] = {}
        for target in self.build_targets:

            if target not in self.supported_koji_targets:
                msg = f"Target not supported: {target}"
                self.report_status_to_all_for_chroot(
                    state=BaseCommitStatus.error,
                    description=msg,
                    url=get_srpm_build_info_url(self.srpm_model.id),
                    chroot=target,
                )
                errors[target] = msg
                continue

            try:
                build_id, web_url = self.run_build(target=target)
            except Exception as ex:
                sentry_integration.send_to_sentry(ex)
                # TODO: Where can we show more info about failure?
                # TODO: Retry
                self.report_status_to_all_for_chroot(
                    state=BaseCommitStatus.error,
                    description=f"Submit of the build failed: {ex}",
                    url=get_srpm_build_info_url(self.srpm_model.id),
                    chroot=target,
                )
                errors[target] = str(ex)
                continue

            koji_build = KojiBuildModel.create(
                build_id=str(build_id),
                commit_sha=self.metadata.commit_sha,
                web_url=web_url,
                target=target,
                status="pending",
                run_model=self.run_model,
            )
            url = get_koji_build_info_url(id_=koji_build.id)
            self.report_status_to_all_for_chroot(
                state=BaseCommitStatus.running,
                description="Building RPM ...",
                url=url,
                chroot=target,
            )

        if errors:
            return TaskResults(
                success=False,
                details={
                    "msg": "Koji build submit was not successful for all chroots.",
                    "errors": errors,
                },
            )

        # TODO: release the hounds!
        """
        celery_app.send_task(
            "task.babysit_koji_build",
            args=(build_metadata.build_id,),
            countdown=120,  # do the first check in 120s
        )
        """

        return TaskResults(success=True, details={})
Example #13
0
def test_koji_build_set_status(clean_before_and_after, a_koji_build):
    assert a_koji_build.status == "pending"
    a_koji_build.set_status("awesome")
    assert a_koji_build.status == "awesome"
    b = KojiBuildModel.get_by_build_id(a_koji_build.build_id, TARGET)
    assert b.status == "awesome"
Example #14
0
    def run(self):
        build = KojiBuildModel.get_by_build_id(
            build_id=str(self.koji_task_event.build_id)
        )

        if not build:
            msg = (
                f"Koji build {self.koji_task_event.build_id} not found in the database."
            )
            logger.warning(msg)
            return TaskResults(success=False, details={"msg": msg})

        logger.debug(
            f"Build on {build.target} in koji changed state "
            f"from {self.koji_task_event.old_state} to {self.koji_task_event.state}."
        )

        build.set_build_start_time(
            datetime.utcfromtimestamp(self.koji_task_event.start_time)
            if self.koji_task_event.start_time
            else None
        )

        build.set_build_finished_time(
            datetime.utcfromtimestamp(self.koji_task_event.completion_time)
            if self.koji_task_event.completion_time
            else None
        )

        url = get_koji_build_info_url(build.id)
        build_job_helper = KojiBuildJobHelper(
            service_config=self.service_config,
            package_config=self.package_config,
            project=self.project,
            metadata=self.data,
            db_trigger=self.db_trigger,
            job_config=self.job_config,
        )

        new_commit_status = {
            KojiTaskState.free: BaseCommitStatus.pending,
            KojiTaskState.open: BaseCommitStatus.running,
            KojiTaskState.closed: BaseCommitStatus.success,
            KojiTaskState.canceled: BaseCommitStatus.error,
            KojiTaskState.assigned: None,
            KojiTaskState.failed: BaseCommitStatus.failure,
        }.get(self.koji_task_event.state)

        description = {
            KojiTaskState.free: "RPM build has been submitted...",
            KojiTaskState.open: "RPM build is in progress...",
            KojiTaskState.closed: "RPM build succeeded.",
            KojiTaskState.canceled: "RPM build was canceled.",
            KojiTaskState.assigned: None,
            KojiTaskState.failed: "RPM build failed.",
        }.get(self.koji_task_event.state)

        if not (new_commit_status and description):
            logger.debug(
                f"We don't react to this koji build state change: {self.koji_task_event.state}"
            )
        else:
            build.set_status(new_commit_status.value)
            build_job_helper.report_status_to_all_for_chroot(
                description=description,
                state=new_commit_status,
                url=url,
                chroot=build.target,
            )

        koji_build_logs = KojiTaskEvent.get_koji_build_logs_url(
            rpm_build_task_id=int(build.build_id),
            koji_logs_url=self.service_config.koji_logs_url,
        )
        build.set_build_logs_url(koji_build_logs)
        koji_rpm_task_web_url = KojiTaskEvent.get_koji_rpm_build_web_url(
            rpm_build_task_id=int(build.build_id),
            koji_web_url=self.service_config.koji_web_url,
        )
        build.set_web_url(koji_rpm_task_web_url)

        msg = (
            f"Build on {build.target} in koji changed state "
            f"from {self.koji_task_event.old_state} to {self.koji_task_event.state}."
        )
        return TaskResults(success=True, details={"msg": msg})