注册服务
This commit is contained in:
Binary file not shown.
@@ -37,6 +37,9 @@ class Settings(BaseSettings):
|
||||
# API配置
|
||||
API_V1_STR: str = "/api/v1"
|
||||
|
||||
# 部署配置
|
||||
DEPLOYMENT_MODE: str = "local" # 部署模式:docker 或 local
|
||||
|
||||
# Gitea 配置
|
||||
GITEA_SERVER_URL: str = ""
|
||||
GITEA_ACCESS_TOKEN: str = ""
|
||||
|
||||
Binary file not shown.
@@ -148,9 +148,6 @@ class ServiceGroup(Base):
|
||||
status = Column(String, default="active", index=True) # 状态
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# 关系
|
||||
services = relationship("AlgorithmService", back_populates="group")
|
||||
|
||||
|
||||
class AlgorithmService(Base):
|
||||
@@ -159,7 +156,6 @@ class AlgorithmService(Base):
|
||||
|
||||
id = Column(String, primary_key=True, index=True)
|
||||
service_id = Column(String, unique=True, nullable=False, index=True) # 服务ID
|
||||
group_id = Column(String, ForeignKey("service_groups.id"), nullable=True, index=True) # 分组ID
|
||||
name = Column(String, nullable=False, index=True) # 服务名称
|
||||
algorithm_name = Column(String, nullable=False) # 算法名称
|
||||
version = Column(String, nullable=False) # 版本
|
||||
@@ -172,9 +168,6 @@ class AlgorithmService(Base):
|
||||
last_heartbeat = Column(DateTime(timezone=True), nullable=True) # 最后心跳时间
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||
|
||||
# 关系
|
||||
group = relationship("ServiceGroup", back_populates="services")
|
||||
|
||||
|
||||
# 添加Algorithm模型的repository关系
|
||||
|
||||
@@ -6,6 +6,7 @@ from pydantic import BaseModel
|
||||
import uuid
|
||||
import os
|
||||
|
||||
from app.config.settings import settings
|
||||
from app.models.models import AlgorithmService, ServiceGroup, AlgorithmRepository
|
||||
from app.models.database import SessionLocal
|
||||
from app.routes.user import get_current_active_user
|
||||
@@ -42,7 +43,7 @@ class ServiceResponse(BaseModel):
|
||||
api_url: str
|
||||
status: str
|
||||
created_at: str
|
||||
updated_at: str
|
||||
updated_at: Optional[str]
|
||||
|
||||
|
||||
class ServiceListResponse(BaseModel):
|
||||
@@ -127,7 +128,7 @@ class BatchOperationResponse(BaseModel):
|
||||
# 初始化服务组件
|
||||
project_analyzer = ProjectAnalyzer()
|
||||
service_generator = ServiceGenerator()
|
||||
service_orchestrator = ServiceOrchestrator()
|
||||
service_orchestrator = ServiceOrchestrator(deployment_mode=settings.DEPLOYMENT_MODE)
|
||||
|
||||
|
||||
@router.post("/register", status_code=status.HTTP_201_CREATED)
|
||||
@@ -137,7 +138,9 @@ async def register_service(
|
||||
):
|
||||
"""注册新服务"""
|
||||
# 检查用户权限
|
||||
if current_user.role_name != "admin":
|
||||
print(f"用户角色: {current_user.role_name}")
|
||||
print(f"用户角色对象: {current_user.role}")
|
||||
if not hasattr(current_user, 'role_name') or current_user.role_name != "admin":
|
||||
raise HTTPException(status_code=403, detail="Insufficient permissions")
|
||||
|
||||
# 创建数据库会话
|
||||
@@ -236,7 +239,7 @@ def main(data):
|
||||
"api_url": new_service.api_url,
|
||||
"status": new_service.status,
|
||||
"created_at": new_service.created_at.isoformat(),
|
||||
"updated_at": new_service.updated_at.isoformat()
|
||||
"updated_at": new_service.updated_at.isoformat() if new_service.updated_at else None
|
||||
}
|
||||
}
|
||||
finally:
|
||||
@@ -272,7 +275,7 @@ async def list_services(
|
||||
api_url=service.api_url,
|
||||
status=service.status,
|
||||
created_at=service.created_at.isoformat(),
|
||||
updated_at=service.updated_at.isoformat()
|
||||
updated_at=service.updated_at.isoformat() if service.updated_at else None
|
||||
))
|
||||
|
||||
return ServiceListResponse(
|
||||
@@ -316,7 +319,7 @@ async def get_service(
|
||||
api_url=service.api_url,
|
||||
status=service.status,
|
||||
created_at=service.created_at.isoformat(),
|
||||
updated_at=service.updated_at.isoformat()
|
||||
updated_at=service.updated_at.isoformat() if service.updated_at else None
|
||||
)
|
||||
)
|
||||
finally:
|
||||
|
||||
@@ -39,8 +39,14 @@ async def get_current_active_user(db: Session = Depends(get_db), token: str = De
|
||||
)
|
||||
|
||||
# 使用UserService获取用户信息,避免直接使用User模型
|
||||
print(f"尝试通过用户名获取用户: {username}")
|
||||
user = UserService.get_user_by_username(db, username)
|
||||
print(f"获取用户结果: {user.id if user else 'None'}")
|
||||
if not user:
|
||||
# 尝试直接查询数据库
|
||||
from app.models.models import User as UserModel
|
||||
direct_user = db.query(UserModel).filter(UserModel.username == username).first()
|
||||
print(f"直接查询数据库结果: {direct_user.id if direct_user else 'None'}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
@@ -51,19 +57,38 @@ async def get_current_active_user(db: Session = Depends(get_db), token: str = De
|
||||
if user.status != "active":
|
||||
raise HTTPException(status_code=400, detail="Inactive user")
|
||||
|
||||
# 使用UserService获取角色信息
|
||||
role = UserService.get_role_by_id(db, user.role_id)
|
||||
|
||||
# 构建角色响应
|
||||
role_response = None
|
||||
if role:
|
||||
role_response = RoleResponse(
|
||||
id=role.id,
|
||||
name=role.name,
|
||||
description=role.description,
|
||||
created_at=role.created_at,
|
||||
updated_at=role.updated_at
|
||||
)
|
||||
role_name = None
|
||||
|
||||
# 尝试获取角色信息
|
||||
try:
|
||||
# 先尝试使用预加载的角色
|
||||
if hasattr(user, 'role') and user.role:
|
||||
role = user.role
|
||||
role_response = RoleResponse(
|
||||
id=role.id,
|
||||
name=role.name,
|
||||
description=role.description,
|
||||
created_at=role.created_at,
|
||||
updated_at=role.updated_at
|
||||
)
|
||||
role_name = role.name
|
||||
else:
|
||||
# 如果没有预加载角色,尝试通过role_id获取
|
||||
role = UserService.get_role_by_id(db, user.role_id)
|
||||
if role:
|
||||
role_response = RoleResponse(
|
||||
id=role.id,
|
||||
name=role.name,
|
||||
description=role.description,
|
||||
created_at=role.created_at,
|
||||
updated_at=role.updated_at
|
||||
)
|
||||
role_name = role.name
|
||||
except Exception as e:
|
||||
# 角色获取失败不影响用户认证
|
||||
print(f"获取角色信息失败: {e}")
|
||||
|
||||
# 构建用户响应
|
||||
user_response = UserResponse(
|
||||
@@ -75,7 +100,7 @@ async def get_current_active_user(db: Session = Depends(get_db), token: str = De
|
||||
created_at=user.created_at,
|
||||
updated_at=user.updated_at,
|
||||
role=role_response,
|
||||
role_name=role.name if role else None
|
||||
role_name=role_name
|
||||
)
|
||||
|
||||
return user_response
|
||||
@@ -85,6 +110,13 @@ async def get_current_active_user(db: Session = Depends(get_db), token: str = De
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"获取当前用户失败: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Could not validate credentials",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
|
||||
|
||||
from app.schemas.user import LoginRequest
|
||||
@@ -151,6 +183,61 @@ async def get_users(
|
||||
return {"users": users, "total": len(users)}
|
||||
|
||||
|
||||
# 角色管理API
|
||||
@router.post("/roles", response_model=RoleResponse)
|
||||
async def create_role(
|
||||
role: RoleCreate,
|
||||
current_user: UserResponse = Depends(get_current_active_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""创建角色"""
|
||||
# 只有管理员可以创建角色
|
||||
if current_user.role_name != "admin":
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
# 检查角色名称是否已存在
|
||||
if UserService.get_role_by_name(db, role.name):
|
||||
raise HTTPException(status_code=400, detail="Role name already exists")
|
||||
|
||||
# 创建角色
|
||||
db_role = UserService.create_role(db, role)
|
||||
|
||||
return db_role
|
||||
|
||||
|
||||
@router.get("/roles", response_model=List[RoleResponse])
|
||||
async def get_roles(
|
||||
current_user: UserResponse = Depends(get_current_active_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取角色列表"""
|
||||
# 只有管理员可以查看所有角色
|
||||
if current_user.role_name != "admin":
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
roles = UserService.get_roles(db)
|
||||
|
||||
return roles
|
||||
|
||||
|
||||
@router.get("/roles/{role_id}", response_model=RoleResponse)
|
||||
async def get_role(
|
||||
role_id: str,
|
||||
current_user: UserResponse = Depends(get_current_active_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取角色详情"""
|
||||
# 只有管理员可以查看角色详情
|
||||
if current_user.role_name != "admin":
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
role = UserService.get_role_by_id(db, role_id)
|
||||
if not role:
|
||||
raise HTTPException(status_code=404, detail="Role not found")
|
||||
|
||||
return role
|
||||
|
||||
|
||||
@router.get("/{user_id}", response_model=UserResponse)
|
||||
async def get_user(
|
||||
user_id: str,
|
||||
@@ -218,58 +305,3 @@ async def delete_user(
|
||||
db.commit()
|
||||
|
||||
return {"message": "User deleted successfully"}
|
||||
|
||||
|
||||
# 角色管理API
|
||||
@router.post("/roles", response_model=RoleResponse)
|
||||
async def create_role(
|
||||
role: RoleCreate,
|
||||
current_user: UserResponse = Depends(get_current_active_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""创建角色"""
|
||||
# 只有管理员可以创建角色
|
||||
if current_user.role_name != "admin":
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
# 检查角色名称是否已存在
|
||||
if UserService.get_role_by_name(db, role.name):
|
||||
raise HTTPException(status_code=400, detail="Role name already exists")
|
||||
|
||||
# 创建角色
|
||||
db_role = UserService.create_role(db, role)
|
||||
|
||||
return db_role
|
||||
|
||||
|
||||
@router.get("/roles", response_model=List[RoleResponse])
|
||||
async def get_roles(
|
||||
current_user: UserResponse = Depends(get_current_active_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取角色列表"""
|
||||
# 只有管理员可以查看所有角色
|
||||
if current_user.role_name != "admin":
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
roles = UserService.get_roles(db)
|
||||
|
||||
return roles
|
||||
|
||||
|
||||
@router.get("/roles/{role_id}", response_model=RoleResponse)
|
||||
async def get_role(
|
||||
role_id: str,
|
||||
current_user: UserResponse = Depends(get_current_active_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""获取角色详情"""
|
||||
# 只有管理员可以查看角色详情
|
||||
if current_user.role_name != "admin":
|
||||
raise HTTPException(status_code=403, detail="Not enough permissions")
|
||||
|
||||
role = UserService.get_role_by_id(db, role_id)
|
||||
if not role:
|
||||
raise HTTPException(status_code=404, detail="Role not found")
|
||||
|
||||
return role
|
||||
|
||||
@@ -5,6 +5,9 @@ import json
|
||||
import time
|
||||
import docker
|
||||
import uuid
|
||||
import subprocess
|
||||
import signal
|
||||
import psutil
|
||||
from typing import Dict, Any, Optional
|
||||
from docker.errors import DockerException, NotFound
|
||||
|
||||
@@ -12,17 +15,28 @@ from docker.errors import DockerException, NotFound
|
||||
class ServiceOrchestrator:
|
||||
"""服务编排服务"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化服务编排器"""
|
||||
try:
|
||||
# 连接Docker客户端
|
||||
self.client = docker.from_env()
|
||||
# 测试连接
|
||||
self.client.ping()
|
||||
print("Docker连接成功")
|
||||
except DockerException as e:
|
||||
print(f"Docker连接失败: {e}")
|
||||
def __init__(self, deployment_mode="local"):
|
||||
"""初始化服务编排器
|
||||
|
||||
Args:
|
||||
deployment_mode: 部署模式,支持"docker"和"local"
|
||||
"""
|
||||
self.deployment_mode = deployment_mode
|
||||
self.processes = {} # 存储本地进程信息
|
||||
|
||||
if deployment_mode == "docker":
|
||||
try:
|
||||
# 连接Docker客户端
|
||||
self.client = docker.from_env()
|
||||
# 测试连接
|
||||
self.client.ping()
|
||||
print("Docker连接成功")
|
||||
except DockerException as e:
|
||||
print(f"Docker连接失败: {e}")
|
||||
self.client = None
|
||||
else:
|
||||
self.client = None
|
||||
print("使用本地进程部署模式")
|
||||
|
||||
def deploy_service(self, service_id: str, service_config: Dict[str, Any], project_info: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""部署服务
|
||||
@@ -36,44 +50,78 @@ class ServiceOrchestrator:
|
||||
部署结果
|
||||
"""
|
||||
try:
|
||||
if not self.client:
|
||||
if self.deployment_mode == "docker":
|
||||
if not self.client:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"service_id": service_id,
|
||||
"container_id": None,
|
||||
"status": "error",
|
||||
"api_url": None
|
||||
}
|
||||
|
||||
# 1. 构建Docker镜像
|
||||
image_name = self._build_docker_image(service_id, project_info, service_config)
|
||||
|
||||
# 2. 启动服务容器
|
||||
container_id = self._start_service_container(service_id, image_name, service_config)
|
||||
|
||||
# 3. 验证服务启动
|
||||
if not self._verify_service_startup(container_id, service_config):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务启动验证失败",
|
||||
"service_id": service_id,
|
||||
"container_id": container_id,
|
||||
"status": "error",
|
||||
"api_url": None
|
||||
}
|
||||
|
||||
# 4. 构建API URL
|
||||
api_url = f"http://{service_config.get('host', 'localhost')}:{service_config.get('port', 8000)}"
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"service_id": service_id,
|
||||
"container_id": None,
|
||||
"status": "error",
|
||||
"api_url": None
|
||||
}
|
||||
|
||||
# 1. 构建Docker镜像
|
||||
image_name = self._build_docker_image(service_id, project_info, service_config)
|
||||
|
||||
# 2. 启动服务容器
|
||||
container_id = self._start_service_container(service_id, image_name, service_config)
|
||||
|
||||
# 3. 验证服务启动
|
||||
if not self._verify_service_startup(container_id, service_config):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务启动验证失败",
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"container_id": container_id,
|
||||
"status": "error",
|
||||
"api_url": None
|
||||
"status": "running",
|
||||
"api_url": api_url,
|
||||
"error": None
|
||||
}
|
||||
else:
|
||||
# 本地进程部署
|
||||
# 1. 创建服务目录
|
||||
service_dir = self._create_service_directory(service_id)
|
||||
|
||||
# 2. 生成服务包装器
|
||||
self._generate_local_service_wrapper(service_dir, project_info, service_config)
|
||||
|
||||
# 3. 启动服务进程
|
||||
process_info = self._start_local_service_process(service_id, service_dir, project_info, service_config)
|
||||
|
||||
# 4. 验证服务启动
|
||||
if not self._verify_local_service_startup(service_id, service_config):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务启动验证失败",
|
||||
"service_id": service_id,
|
||||
"container_id": None,
|
||||
"status": "error",
|
||||
"api_url": None
|
||||
}
|
||||
|
||||
# 5. 构建API URL
|
||||
api_url = f"http://{service_config.get('host', 'localhost')}:{service_config.get('port', 8000)}"
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"container_id": service_id, # 使用服务ID作为容器ID
|
||||
"status": "running",
|
||||
"api_url": api_url,
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 4. 构建API URL
|
||||
api_url = f"http://{service_config.get('host', 'localhost')}:{service_config.get('port', 8000)}"
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"container_id": container_id,
|
||||
"status": "running",
|
||||
"api_url": api_url,
|
||||
"error": None
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -95,35 +143,85 @@ class ServiceOrchestrator:
|
||||
启动结果
|
||||
"""
|
||||
try:
|
||||
if not self.client:
|
||||
if self.deployment_mode == "docker":
|
||||
if not self.client:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 启动容器
|
||||
container.start()
|
||||
|
||||
# 验证服务启动
|
||||
if not self._verify_service_health(container_id):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务健康检查失败",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
"status": "running",
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 启动容器
|
||||
container.start()
|
||||
|
||||
# 验证服务启动
|
||||
if not self._verify_service_health(container_id):
|
||||
else:
|
||||
# 本地进程启动
|
||||
if service_id not in self.processes:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务不存在",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
process_info = self.processes[service_id]
|
||||
|
||||
# 检查进程是否已经在运行
|
||||
if process_info.get("pid"):
|
||||
try:
|
||||
process = psutil.Process(process_info["pid"])
|
||||
if process.is_running():
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "running",
|
||||
"error": None
|
||||
}
|
||||
except:
|
||||
pass
|
||||
|
||||
# 重新启动进程
|
||||
service_dir = process_info["service_dir"]
|
||||
project_info = process_info["project_info"]
|
||||
service_config = process_info["service_config"]
|
||||
|
||||
# 启动服务进程
|
||||
new_process_info = self._start_local_service_process(service_id, service_dir, project_info, service_config)
|
||||
|
||||
# 验证服务启动
|
||||
if not self._verify_local_service_startup(service_id, service_config):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务启动验证失败",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务健康检查失败",
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
"status": "running",
|
||||
"error": None
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "running",
|
||||
"error": None
|
||||
}
|
||||
except NotFound:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -150,26 +248,58 @@ class ServiceOrchestrator:
|
||||
停止结果
|
||||
"""
|
||||
try:
|
||||
if not self.client:
|
||||
if self.deployment_mode == "docker":
|
||||
if not self.client:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 停止容器
|
||||
container.stop(timeout=30)
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
"status": "stopped",
|
||||
"error": None
|
||||
}
|
||||
else:
|
||||
# 本地进程停止
|
||||
if service_id not in self.processes:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务不存在",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
process_info = self.processes[service_id]
|
||||
|
||||
# 停止进程
|
||||
if process_info.get("pid"):
|
||||
try:
|
||||
process = psutil.Process(process_info["pid"])
|
||||
if process.is_running():
|
||||
process.terminate()
|
||||
process.wait(timeout=30)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 更新进程状态
|
||||
self.processes[service_id]["pid"] = None
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "stopped",
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 停止容器
|
||||
container.stop(timeout=30)
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "stopped",
|
||||
"error": None
|
||||
}
|
||||
except NotFound:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -196,35 +326,81 @@ class ServiceOrchestrator:
|
||||
重启结果
|
||||
"""
|
||||
try:
|
||||
if not self.client:
|
||||
if self.deployment_mode == "docker":
|
||||
if not self.client:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 重启容器
|
||||
container.restart(timeout=30)
|
||||
|
||||
# 验证服务启动
|
||||
if not self._verify_service_health(container_id):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务健康检查失败",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
"status": "running",
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 重启容器
|
||||
container.restart(timeout=30)
|
||||
|
||||
# 验证服务启动
|
||||
if not self._verify_service_health(container_id):
|
||||
else:
|
||||
# 本地进程重启
|
||||
if service_id not in self.processes:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务不存在",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
process_info = self.processes[service_id]
|
||||
|
||||
# 停止当前进程
|
||||
if process_info.get("pid"):
|
||||
try:
|
||||
process = psutil.Process(process_info["pid"])
|
||||
if process.is_running():
|
||||
process.terminate()
|
||||
process.wait(timeout=30)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 重新启动进程
|
||||
service_dir = process_info["service_dir"]
|
||||
project_info = process_info["project_info"]
|
||||
service_config = process_info["service_config"]
|
||||
|
||||
# 启动服务进程
|
||||
new_process_info = self._start_local_service_process(service_id, service_dir, project_info, service_config)
|
||||
|
||||
# 验证服务启动
|
||||
if not self._verify_local_service_startup(service_id, service_config):
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务启动验证失败",
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
}
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务健康检查失败",
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "error"
|
||||
"status": "running",
|
||||
"error": None
|
||||
}
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"status": "running",
|
||||
"error": None
|
||||
}
|
||||
except NotFound:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -252,34 +428,72 @@ class ServiceOrchestrator:
|
||||
删除结果
|
||||
"""
|
||||
try:
|
||||
if not self.client:
|
||||
if self.deployment_mode == "docker":
|
||||
if not self.client:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"service_id": service_id
|
||||
}
|
||||
|
||||
# 停止并删除容器
|
||||
if container_id:
|
||||
try:
|
||||
container = self.client.containers.get(container_id)
|
||||
container.stop(timeout=10)
|
||||
container.remove(force=True)
|
||||
except NotFound:
|
||||
pass
|
||||
|
||||
# 删除镜像
|
||||
if image_name:
|
||||
try:
|
||||
self.client.images.remove(image_name, force=True)
|
||||
except:
|
||||
pass
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"service_id": service_id
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 停止并删除容器
|
||||
if container_id:
|
||||
else:
|
||||
# 本地进程删除
|
||||
if service_id not in self.processes:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务不存在",
|
||||
"service_id": service_id
|
||||
}
|
||||
|
||||
process_info = self.processes[service_id]
|
||||
|
||||
# 停止进程
|
||||
if process_info.get("pid"):
|
||||
try:
|
||||
process = psutil.Process(process_info["pid"])
|
||||
if process.is_running():
|
||||
process.terminate()
|
||||
process.wait(timeout=30)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 删除服务目录
|
||||
service_dir = process_info["service_dir"]
|
||||
try:
|
||||
container = self.client.containers.get(container_id)
|
||||
container.stop(timeout=10)
|
||||
container.remove(force=True)
|
||||
except NotFound:
|
||||
pass
|
||||
|
||||
# 删除镜像
|
||||
if image_name:
|
||||
try:
|
||||
self.client.images.remove(image_name, force=True)
|
||||
import shutil
|
||||
shutil.rmtree(service_dir)
|
||||
except:
|
||||
pass
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 从进程列表中删除
|
||||
del self.processes[service_id]
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"service_id": service_id,
|
||||
"error": None
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -297,34 +511,78 @@ class ServiceOrchestrator:
|
||||
服务状态
|
||||
"""
|
||||
try:
|
||||
if not self.client:
|
||||
if self.deployment_mode == "docker":
|
||||
if not self.client:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"status": "unknown",
|
||||
"health": "unknown"
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 获取容器状态
|
||||
status = container.status
|
||||
|
||||
# 检查服务健康状态
|
||||
health = "unknown"
|
||||
if status == "running":
|
||||
if self._verify_service_health(container_id):
|
||||
health = "healthy"
|
||||
else:
|
||||
health = "unhealthy"
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"status": "unknown",
|
||||
"health": "unknown"
|
||||
"success": True,
|
||||
"status": status,
|
||||
"health": health,
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 获取容器状态
|
||||
status = container.status
|
||||
|
||||
# 检查服务健康状态
|
||||
health = "unknown"
|
||||
if status == "running":
|
||||
if self._verify_service_health(container_id):
|
||||
health = "healthy"
|
||||
else:
|
||||
# 本地进程状态查询
|
||||
# 假设container_id就是service_id
|
||||
service_id = container_id
|
||||
|
||||
if service_id not in self.processes:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务不存在",
|
||||
"status": "not_found",
|
||||
"health": "unknown"
|
||||
}
|
||||
|
||||
process_info = self.processes[service_id]
|
||||
|
||||
# 检查进程状态
|
||||
status = "unknown"
|
||||
health = "unknown"
|
||||
|
||||
if process_info.get("pid"):
|
||||
try:
|
||||
process = psutil.Process(process_info["pid"])
|
||||
if process.is_running():
|
||||
status = "running"
|
||||
# 检查服务健康状态
|
||||
service_config = process_info["service_config"]
|
||||
if self._verify_local_service_health(service_id, service_config):
|
||||
health = "healthy"
|
||||
else:
|
||||
health = "unhealthy"
|
||||
else:
|
||||
status = "stopped"
|
||||
except:
|
||||
status = "stopped"
|
||||
else:
|
||||
health = "unhealthy"
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"status": status,
|
||||
"health": health,
|
||||
"error": None
|
||||
}
|
||||
status = "stopped"
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"status": status,
|
||||
"health": health,
|
||||
"error": None
|
||||
}
|
||||
except NotFound:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -351,24 +609,63 @@ class ServiceOrchestrator:
|
||||
服务日志
|
||||
"""
|
||||
try:
|
||||
if not self.client:
|
||||
if self.deployment_mode == "docker":
|
||||
if not self.client:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"logs": []
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 获取日志
|
||||
logs = container.logs(tail=lines).decode('utf-8').split('\n')
|
||||
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Docker连接失败",
|
||||
"logs": []
|
||||
"success": True,
|
||||
"logs": logs,
|
||||
"error": None
|
||||
}
|
||||
else:
|
||||
# 本地进程日志获取
|
||||
# 假设container_id就是service_id
|
||||
service_id = container_id
|
||||
|
||||
if service_id not in self.processes:
|
||||
return {
|
||||
"success": False,
|
||||
"error": "服务不存在",
|
||||
"logs": []
|
||||
}
|
||||
|
||||
process_info = self.processes[service_id]
|
||||
|
||||
# 获取日志文件路径
|
||||
log_file = process_info.get("log_file")
|
||||
|
||||
if not log_file or not os.path.exists(log_file):
|
||||
return {
|
||||
"success": True,
|
||||
"logs": [],
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 读取日志文件
|
||||
try:
|
||||
with open(log_file, 'r') as f:
|
||||
logs = f.readlines()
|
||||
# 只返回最后lines行
|
||||
logs = [line.rstrip('\n') for line in logs[-lines:]]
|
||||
except:
|
||||
logs = []
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"logs": logs,
|
||||
"error": None
|
||||
}
|
||||
|
||||
# 获取容器
|
||||
container = self.client.containers.get(container_id)
|
||||
|
||||
# 获取日志
|
||||
logs = container.logs(tail=lines).decode('utf-8').split('\n')
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"logs": logs,
|
||||
"error": None
|
||||
}
|
||||
except NotFound:
|
||||
return {
|
||||
"success": False,
|
||||
@@ -960,3 +1257,135 @@ json
|
||||
}
|
||||
with open(os.path.join(build_context, "package.json"), "w") as f:
|
||||
json.dump(package_data, f, indent=2)
|
||||
|
||||
def _create_service_directory(self, service_id: str) -> str:
|
||||
"""创建服务目录
|
||||
|
||||
Args:
|
||||
service_id: 服务ID
|
||||
|
||||
Returns:
|
||||
服务目录路径
|
||||
"""
|
||||
service_dir = os.path.join("/tmp", f"algorithm-service-{service_id}")
|
||||
os.makedirs(service_dir, exist_ok=True)
|
||||
return service_dir
|
||||
|
||||
def _generate_local_service_wrapper(self, service_dir: str, project_info: Dict[str, Any], service_config: Dict[str, Any]):
|
||||
"""生成本地服务包装器
|
||||
|
||||
Args:
|
||||
service_dir: 服务目录
|
||||
project_info: 项目信息
|
||||
service_config: 服务配置
|
||||
"""
|
||||
# 生成服务包装器
|
||||
service_wrapper_content = self._generate_service_wrapper(project_info, service_config)
|
||||
wrapper_extension = ".py" if project_info["project_type"] == "python" else ".js"
|
||||
with open(os.path.join(service_dir, f"service_wrapper{wrapper_extension}"), "w") as f:
|
||||
f.write(service_wrapper_content)
|
||||
|
||||
# 创建模拟的算法文件
|
||||
algorithm_content = """
|
||||
def predict(data):
|
||||
return {"result": "Prediction result", "input": data}
|
||||
|
||||
def run(data):
|
||||
return {"result": "Run result", "input": data}
|
||||
|
||||
def main(data):
|
||||
return {"result": "Main result", "input": data}
|
||||
"""
|
||||
with open(os.path.join(service_dir, "algorithm.py"), "w") as f:
|
||||
f.write(algorithm_content)
|
||||
|
||||
def _start_local_service_process(self, service_id: str, service_dir: str, project_info: Dict[str, Any], service_config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""启动本地服务进程
|
||||
|
||||
Args:
|
||||
service_id: 服务ID
|
||||
service_dir: 服务目录
|
||||
project_info: 项目信息
|
||||
service_config: 服务配置
|
||||
|
||||
Returns:
|
||||
进程信息
|
||||
"""
|
||||
# 创建日志文件
|
||||
log_file = os.path.join(service_dir, f"service_{service_id}.log")
|
||||
|
||||
# 构建启动命令
|
||||
if project_info["project_type"] == "python":
|
||||
cmd = ["python", f"service_wrapper.py"]
|
||||
else:
|
||||
cmd = ["node", f"service_wrapper.js"]
|
||||
|
||||
# 设置环境变量
|
||||
env = os.environ.copy()
|
||||
env["HOST"] = service_config.get("host", "0.0.0.0")
|
||||
env["PORT"] = str(service_config.get("port", 8000))
|
||||
env["TIMEOUT"] = str(service_config.get("timeout", 30))
|
||||
|
||||
# 启动进程
|
||||
process = subprocess.Popen(
|
||||
cmd,
|
||||
cwd=service_dir,
|
||||
env=env,
|
||||
stdout=open(log_file, "a"),
|
||||
stderr=subprocess.STDOUT,
|
||||
start_new_session=True
|
||||
)
|
||||
|
||||
# 保存进程信息
|
||||
process_info = {
|
||||
"pid": process.pid,
|
||||
"service_dir": service_dir,
|
||||
"log_file": log_file,
|
||||
"project_info": project_info,
|
||||
"service_config": service_config
|
||||
}
|
||||
|
||||
self.processes[service_id] = process_info
|
||||
|
||||
return process_info
|
||||
|
||||
def _verify_local_service_startup(self, service_id: str, service_config: Dict[str, Any]) -> bool:
|
||||
"""验证本地服务启动
|
||||
|
||||
Args:
|
||||
service_id: 服务ID
|
||||
service_config: 服务配置
|
||||
|
||||
Returns:
|
||||
是否启动成功
|
||||
"""
|
||||
# 等待服务启动
|
||||
time.sleep(5)
|
||||
|
||||
# 验证服务健康状态
|
||||
return self._verify_local_service_health(service_id, service_config)
|
||||
|
||||
def _verify_local_service_health(self, service_id: str, service_config: Dict[str, Any]) -> bool:
|
||||
"""验证本地服务健康状态
|
||||
|
||||
Args:
|
||||
service_id: 服务ID
|
||||
service_config: 服务配置
|
||||
|
||||
Returns:
|
||||
是否健康
|
||||
"""
|
||||
try:
|
||||
import requests
|
||||
|
||||
# 构建健康检查URL
|
||||
host = service_config.get("host", "localhost")
|
||||
port = service_config.get("port", 8000)
|
||||
health_check_url = f"http://{host}:{port}/health"
|
||||
|
||||
# 发送健康检查请求
|
||||
response = requests.get(health_check_url, timeout=10)
|
||||
|
||||
return response.status_code == 200
|
||||
except:
|
||||
return False
|
||||
|
||||
Reference in New Issue
Block a user