""" Jenkins 任务处理 使用 Celery 处理异步 Jenkins 任务触发 """ import asyncio import time from typing import Dict, Any from datetime import datetime import structlog from celery import Celery, Task import httpx from app.config import get_settings from app.services.jenkins_service import JenkinsService logger = structlog.get_logger() settings = get_settings() # 创建 Celery 应用 celery_app = Celery( "gitea_webhook_ambassador", broker=settings.redis.url, backend=settings.redis.url, include=["app.tasks.jenkins_tasks"] ) # Celery 配置 celery_app.conf.update( task_serializer="json", accept_content=["json"], result_serializer="json", timezone="UTC", enable_utc=True, task_track_started=True, task_time_limit=300, # 5分钟超时 task_soft_time_limit=240, # 4分钟软超时 worker_prefetch_multiplier=1, worker_max_tasks_per_child=1000, worker_max_memory_per_child=200000, # 200MB task_acks_late=True, task_reject_on_worker_lost=True, task_always_eager=False, # 生产环境设为 False result_expires=3600, # 结果缓存1小时 ) class JenkinsTask(Task): """Jenkins 任务基类""" abstract = True def __init__(self): self.jenkins_service = None def __call__(self, *args, **kwargs): if self.jenkins_service is None: self.jenkins_service = JenkinsService() return self.run(*args, **kwargs) def on_failure(self, exc, task_id, args, kwargs, einfo): """任务失败回调""" logger.error("Task failed", task_id=task_id, task_name=self.name, error=str(exc), args=args, kwargs=kwargs) def on_retry(self, exc, task_id, args, kwargs, einfo): """任务重试回调""" logger.warning("Task retrying", task_id=task_id, task_name=self.name, error=str(exc), retry_count=self.request.retries) def on_success(self, retval, task_id, args, kwargs): """任务成功回调""" logger.info("Task completed successfully", task_id=task_id, task_name=self.name, result=retval) @celery_app.task( bind=True, base=JenkinsTask, max_retries=3, default_retry_delay=60, autoretry_for=(Exception,), retry_backoff=True, retry_jitter=True ) def trigger_jenkins_job( self, job_name: str, jenkins_url: str, parameters: Dict[str, str], event_id: str, repository: str, branch: str, commit_hash: str, priority: int = 1 ) -> Dict[str, Any]: """ 触发 Jenkins 任务 Args: job_name: Jenkins 任务名 jenkins_url: Jenkins URL parameters: 任务参数 event_id: 事件 ID repository: 仓库名 branch: 分支名 commit_hash: 提交哈希 priority: 优先级 Returns: Dict: 任务执行结果 """ start_time = time.time() try: logger.info("Starting Jenkins job trigger", task_id=self.request.id, job_name=job_name, jenkins_url=jenkins_url, repository=repository, branch=branch, commit_hash=commit_hash, priority=priority) # 创建 Jenkins 服务实例 jenkins_service = JenkinsService() # 触发 Jenkins 任务 result = asyncio.run(jenkins_service.trigger_job( job_name=job_name, jenkins_url=jenkins_url, parameters=parameters )) execution_time = time.time() - start_time if result["success"]: logger.info("Jenkins job triggered successfully", task_id=self.request.id, job_name=job_name, build_number=result.get("build_number"), execution_time=execution_time) return { "success": True, "task_id": self.request.id, "job_name": job_name, "jenkins_url": jenkins_url, "build_number": result.get("build_number"), "build_url": result.get("build_url"), "event_id": event_id, "repository": repository, "branch": branch, "commit_hash": commit_hash, "execution_time": execution_time, "timestamp": datetime.utcnow().isoformat() } else: logger.error("Jenkins job trigger failed", task_id=self.request.id, job_name=job_name, error=result.get("error"), execution_time=execution_time) # 重试任务 raise self.retry( countdown=settings.queue.retry_delay * (2 ** self.request.retries), max_retries=settings.queue.max_retries ) except Exception as e: execution_time = time.time() - start_time logger.error("Unexpected error in Jenkins task", task_id=self.request.id, job_name=job_name, error=str(e), execution_time=execution_time) # 重试任务 raise self.retry( countdown=settings.queue.retry_delay * (2 ** self.request.retries), max_retries=settings.queue.max_retries ) @celery_app.task( bind=True, base=JenkinsTask, max_retries=2, default_retry_delay=30 ) def check_jenkins_health( self, jenkins_url: str ) -> Dict[str, Any]: """ 检查 Jenkins 健康状态 Args: jenkins_url: Jenkins URL Returns: Dict: 健康检查结果 """ try: logger.info("Checking Jenkins health", jenkins_url=jenkins_url) jenkins_service = JenkinsService() result = asyncio.run(jenkins_service.check_health(jenkins_url)) return { "success": True, "jenkins_url": jenkins_url, "healthy": result.get("healthy", False), "response_time": result.get("response_time"), "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error("Jenkins health check failed", jenkins_url=jenkins_url, error=str(e)) return { "success": False, "jenkins_url": jenkins_url, "error": str(e), "timestamp": datetime.utcnow().isoformat() } @celery_app.task( bind=True, base=JenkinsTask ) def cleanup_expired_tasks(self) -> Dict[str, Any]: """ 清理过期任务 Returns: Dict: 清理结果 """ try: logger.info("Starting task cleanup") # 获取所有任务 inspect = self.app.control.inspect() # 清理过期的结果 cleaned_count = 0 current_time = time.time() # 这里可以添加更复杂的清理逻辑 # 比如清理超过一定时间的任务结果 logger.info("Task cleanup completed", cleaned_count=cleaned_count) return { "success": True, "cleaned_count": cleaned_count, "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error("Task cleanup failed", error=str(e)) return { "success": False, "error": str(e), "timestamp": datetime.utcnow().isoformat() } # 定时任务 @celery_app.on_after_configure.connect def setup_periodic_tasks(sender, **kwargs): """设置定时任务""" # 每小时清理过期任务 sender.add_periodic_task( 3600.0, # 1小时 cleanup_expired_tasks.s(), name="cleanup-expired-tasks" ) # 每5分钟检查 Jenkins 健康状态 for env_name, env_config in settings.environments.items(): sender.add_periodic_task( 300.0, # 5分钟 check_jenkins_health.s(env_config.jenkins_url), name=f"check-jenkins-health-{env_name}" ) def get_celery_app() -> Celery: """获取 Celery 应用实例""" return celery_app