def __init__( self, package_config: PackageConfig, job_config: JobConfig, event: dict, ): # build helper needs package_config to resolve dependencies b/w tests and build jobs self.package_config = package_config # always use job_config to pick up values, use package_config only for package_config.jobs self.job_config = job_config self.data = EventData.from_event_dict(event) self.pushgateway = Pushgateway() self._db_trigger: Optional[AbstractTriggerDbType] = None self._project: Optional[GitProject] = None self._clean_workplace()
def push_initial_metrics(event: Event, handler: JobHandler, build_targets_len: int): pushgateway = Pushgateway() task_accepted_time = datetime.now(timezone.utc) response_time = measure_time(end=task_accepted_time, begin=event.created_at) pushgateway.initial_status_time.observe(response_time) if response_time > 15: pushgateway.no_status_after_15_s.inc() # set the time when the accepted status was set so that we can use it later for measurements event.task_accepted_time = task_accepted_time if isinstance(handler, CoprBuildHandler): for _ in range(build_targets_len): pushgateway.copr_builds_queued.inc() pushgateway.push()
class JobHandler(Handler): """Generic interface to handle different type of inputs""" task_name: TaskName def __init__( self, package_config: PackageConfig, job_config: JobConfig, event: dict, ): # build helper needs package_config to resolve dependencies b/w tests and build jobs self.package_config = package_config # always use job_config to pick up values, use package_config only for package_config.jobs self.job_config = job_config self.data = EventData.from_event_dict(event) self.pushgateway = Pushgateway() self._db_trigger: Optional[AbstractTriggerDbType] = None self._project: Optional[GitProject] = None self._clean_workplace() @property def project(self) -> Optional[GitProject]: if not self._project and self.data.project_url: self._project = self.service_config.get_project(url=self.data.project_url) return self._project @classmethod def get_all_subclasses(cls) -> Set[Type["JobHandler"]]: return set(cls.__subclasses__()).union( [s for c in cls.__subclasses__() for s in c.get_all_subclasses()] ) def check_if_actor_can_run_job_and_report(self, actor: str) -> bool: """ Here, handlers can specify additional check of permissions for a given user by overriding this method. In case of False, we expect the method provides a feedback to the user. """ return True def run_job(self): """ If pre-check succeeds, run the job for the specific handler. :return: Dict [str, TaskResults] """ job_type = ( self.job_config.type.value if self.job_config else self.task_name.value ) logger.debug(f"Running handler {str(self)} for {job_type}") job_results: Dict[str, TaskResults] = {} current_time = datetime.now().strftime(DATETIME_FORMAT) result_key = f"{job_type}-{current_time}" job_results[result_key] = self.run_n_clean() logger.debug("Job finished!") for result in job_results.values(): if not (result and result["success"]): logger.error(result["details"]["msg"]) # push the metrics from job self.pushgateway.push() return job_results @classmethod def get_signature(cls, event: Event, job: Optional[JobConfig]) -> Signature: """ Get the signature of a Celery task which will run the handler. https://docs.celeryproject.org/en/stable/userguide/canvas.html#signatures :param event: event which triggered the task :param job: job to process """ logger.debug(f"Getting signature of a Celery task {cls.task_name}.") return signature( cls.task_name.value, kwargs={ "package_config": dump_package_config(event.package_config), "job_config": dump_job_config(job), "event": event.get_dict(), }, ) def run(self) -> TaskResults: raise NotImplementedError("This should have been implemented.")
def run_copr_build(self) -> TaskResults: if not (self.job_build or self.job_tests): msg = "No copr_build or tests job defined." # we can't report it to end-user at this stage return TaskResults(success=False, details={"msg": msg}) self.report_status_to_all( description="Building SRPM ...", state=CommitStatus.pending, # pagure requires "valid url" url="", ) 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: build_id, web_url = self.run_build() Pushgateway().push_copr_build_created() 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( state=CommitStatus.error, description=f"Submit of the build failed: {ex}", ) return TaskResults( success=False, details={"msg": "Submit of the Copr build failed.", "error": str(ex)}, ) unprocessed_chroots = [] for chroot in self.build_targets: if chroot not in self.available_chroots: self.report_status_to_all_for_chroot( state=CommitStatus.error, description=f"Not supported target: {chroot}", url=get_srpm_log_url_from_flask(self.srpm_model.id), chroot=chroot, ) unprocessed_chroots.append(chroot) continue copr_build = CoprBuildModel.get_or_create( build_id=str(build_id), commit_sha=self.metadata.commit_sha, project_name=self.job_project, owner=self.job_owner, web_url=web_url, target=chroot, status="pending", srpm_build=self.srpm_model, trigger_model=self.db_trigger, ) url = get_copr_build_info_url_from_flask(id_=copr_build.id) self.report_status_to_all_for_chroot( state=CommitStatus.pending, description="Starting RPM build...", url=url, chroot=chroot, ) if unprocessed_chroots: unprocessed = "\n".join(sorted(unprocessed_chroots)) available = "\n".join(sorted(self.available_chroots)) self.project.pr_comment( pr_id=self.metadata.pr_id, body="There are build targets that are not supported by COPR.\n" "<details>\n<summary>Unprocessed build targets</summary>\n\n" f"```\n{unprocessed}\n```\n</details>\n" "<details>\n<summary>Available build targets</summary>\n\n" f"```\n{available}\n```\n</details>", ) # release the hounds! celery_app.send_task( "task.babysit_copr_build", args=(build_id,), countdown=120, # do the first check in 120s ) return TaskResults(success=True, details={})