添加qiming-rcoder模块
This commit is contained in:
8
qiming-rcoder/crates/container-runtime-api/src/lib.rs
Normal file
8
qiming-rcoder/crates/container-runtime-api/src/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
//! Container Runtime API
|
||||
//!
|
||||
//! This crate provides the `ContainerRuntime` trait abstraction for different
|
||||
//! container runtimes (Docker, Kubernetes, etc.).
|
||||
|
||||
pub mod runtime_trait;
|
||||
|
||||
pub use runtime_trait::*;
|
||||
305
qiming-rcoder/crates/container-runtime-api/src/runtime_trait.rs
Normal file
305
qiming-rcoder/crates/container-runtime-api/src/runtime_trait.rs
Normal file
@@ -0,0 +1,305 @@
|
||||
//! Container runtime abstraction trait
|
||||
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use shared_types::{ContainerBasicInfo, ServiceResourceLimits, ServiceType};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Container runtime errors
|
||||
#[derive(Error, Debug)]
|
||||
pub enum ContainerRuntimeError {
|
||||
#[error("Connection error: {0}")]
|
||||
ConnectionError(String),
|
||||
|
||||
#[error("Container creation failed: {0}")]
|
||||
ContainerCreationError(String),
|
||||
|
||||
#[error("Container start failed: {0}")]
|
||||
ContainerStartError(String),
|
||||
|
||||
#[error("Container stop failed: {0}")]
|
||||
ContainerStopError(String),
|
||||
|
||||
#[error("Container not found: {0}")]
|
||||
ContainerNotFound(String),
|
||||
|
||||
#[error("Configuration error: {0}")]
|
||||
ConfigurationError(String),
|
||||
|
||||
#[error("Timeout: {0}")]
|
||||
Timeout(String),
|
||||
|
||||
#[error("Kubernetes error: {0}")]
|
||||
K8sError(String),
|
||||
|
||||
#[error("Docker error: {0}")]
|
||||
DockerError(String),
|
||||
}
|
||||
|
||||
/// Result type for container operations
|
||||
pub type ContainerRuntimeResult<T> = Result<T, ContainerRuntimeError>;
|
||||
|
||||
/// Container runtime status
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ContainerRuntimeStatus {
|
||||
Pending,
|
||||
Running,
|
||||
Succeeded,
|
||||
Failed,
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
/// Basic container info returned by runtime
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RuntimeContainerInfo {
|
||||
pub container_id: String,
|
||||
pub container_name: String,
|
||||
pub container_ip: String,
|
||||
pub status: ContainerRuntimeStatus,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// 已被移除的容器信息(用于清理关联资源)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RemovedContainerInfo {
|
||||
/// 容器名称(稳定标识符)
|
||||
pub container_name: String,
|
||||
/// 容器 IP
|
||||
pub container_ip: String,
|
||||
/// 容器标识符(project_id 或 user_id)
|
||||
pub identifier: String,
|
||||
/// 服务类型
|
||||
pub service_type: ServiceType,
|
||||
}
|
||||
|
||||
impl From<ContainerRuntimeStatus> for String {
|
||||
fn from(status: ContainerRuntimeStatus) -> Self {
|
||||
match status {
|
||||
ContainerRuntimeStatus::Pending => "pending".to_string(),
|
||||
ContainerRuntimeStatus::Running => "running".to_string(),
|
||||
ContainerRuntimeStatus::Succeeded => "succeeded".to_string(),
|
||||
ContainerRuntimeStatus::Failed => "failed".to_string(),
|
||||
ContainerRuntimeStatus::Unknown(s) => s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters for creating a container
|
||||
///
|
||||
/// Bundles all parameters needed for container creation to avoid
|
||||
/// long parameter lists that hurt code readability and maintainability.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ContainerCreateParams {
|
||||
/// Project identifier (used as container name base for RCoder service)
|
||||
pub project_id: Option<String>,
|
||||
/// User identifier (used as container name base for ComputerAgentRunner)
|
||||
pub user_id: Option<String>,
|
||||
/// Workspace path on host
|
||||
pub host_workspace_path: String,
|
||||
/// Service type determining container purpose
|
||||
pub service_type: ServiceType,
|
||||
/// Optional resource constraints
|
||||
pub resource_limits: Option<ServiceResourceLimits>,
|
||||
/// Pod identifier for container reuse (for multi-tenant scenarios)
|
||||
pub pod_id: Option<String>,
|
||||
/// Isolation type: tenant|space|project (for multi-tenant scenarios)
|
||||
pub isolation_type: Option<String>,
|
||||
/// Tenant identifier (for multi-tenant scenarios)
|
||||
pub tenant_id: Option<String>,
|
||||
/// Space identifier (for multi-tenant scenarios)
|
||||
pub space_id: Option<String>,
|
||||
}
|
||||
|
||||
impl ContainerCreateParams {
|
||||
/// Create a new builder for container create params
|
||||
pub fn builder() -> ContainerCreateParamsBuilder {
|
||||
ContainerCreateParamsBuilder::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ContainerCreateParamsBuilder {
|
||||
project_id: Option<String>,
|
||||
user_id: Option<String>,
|
||||
host_workspace_path: Option<String>,
|
||||
service_type: Option<ServiceType>,
|
||||
resource_limits: Option<ServiceResourceLimits>,
|
||||
pod_id: Option<String>,
|
||||
isolation_type: Option<String>,
|
||||
tenant_id: Option<String>,
|
||||
space_id: Option<String>,
|
||||
}
|
||||
|
||||
impl ContainerCreateParamsBuilder {
|
||||
pub fn project_id(mut self, project_id: impl Into<String>) -> Self {
|
||||
self.project_id = Some(project_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn user_id(mut self, user_id: impl Into<String>) -> Self {
|
||||
self.user_id = Some(user_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn host_workspace_path(mut self, host_workspace_path: impl Into<String>) -> Self {
|
||||
self.host_workspace_path = Some(host_workspace_path.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn service_type(mut self, service_type: ServiceType) -> Self {
|
||||
self.service_type = Some(service_type);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn resource_limits(mut self, resource_limits: ServiceResourceLimits) -> Self {
|
||||
self.resource_limits = Some(resource_limits);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn pod_id(mut self, pod_id: impl Into<String>) -> Self {
|
||||
self.pod_id = Some(pod_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn isolation_type(mut self, isolation_type: impl Into<String>) -> Self {
|
||||
self.isolation_type = Some(isolation_type.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn tenant_id(mut self, tenant_id: impl Into<String>) -> Self {
|
||||
self.tenant_id = Some(tenant_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn space_id(mut self, space_id: impl Into<String>) -> Self {
|
||||
self.space_id = Some(space_id.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> ContainerCreateParams {
|
||||
ContainerCreateParams {
|
||||
project_id: self.project_id,
|
||||
user_id: self.user_id,
|
||||
host_workspace_path: self
|
||||
.host_workspace_path
|
||||
.unwrap_or_else(|| String::new()),
|
||||
service_type: self.service_type.unwrap_or(ServiceType::RCoder),
|
||||
resource_limits: self.resource_limits,
|
||||
pod_id: self.pod_id,
|
||||
isolation_type: self.isolation_type,
|
||||
tenant_id: self.tenant_id,
|
||||
space_id: self.space_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Abstraction trait for container runtimes (Docker, Kubernetes, etc.)
|
||||
///
|
||||
/// This trait follows the Interface Segregation Principle - it provides
|
||||
/// a lean interface with only the methods that callers actually need.
|
||||
#[async_trait]
|
||||
pub trait ContainerRuntime: Send + Sync {
|
||||
/// Create and start a container
|
||||
async fn create_container(
|
||||
&self,
|
||||
params: ContainerCreateParams,
|
||||
) -> ContainerRuntimeResult<ContainerBasicInfo>;
|
||||
|
||||
/// Get container information by project_id
|
||||
async fn get_container_info(
|
||||
&self,
|
||||
project_id: &str,
|
||||
) -> ContainerRuntimeResult<Option<ContainerBasicInfo>>;
|
||||
|
||||
/// Get container information by identifier + service type.
|
||||
///
|
||||
/// `identifier` means:
|
||||
/// - RCoder: `project_id`
|
||||
/// - ComputerAgentRunner: `user_id`
|
||||
async fn get_container_info_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
service_type: &ServiceType,
|
||||
) -> ContainerRuntimeResult<Option<ContainerBasicInfo>> {
|
||||
if matches!(service_type, ServiceType::RCoder) {
|
||||
return self.get_container_info(identifier).await;
|
||||
}
|
||||
|
||||
let info = self.find_container(identifier, service_type).await?;
|
||||
Ok(info.map(|pod| ContainerBasicInfo {
|
||||
container_id: pod.container_id,
|
||||
container_name: pod.container_name,
|
||||
container_ip: pod.container_ip.clone(),
|
||||
internal_port: shared_types::GRPC_DEFAULT_PORT,
|
||||
external_port: 0,
|
||||
project_id: identifier.to_string(),
|
||||
status: String::from(pod.status),
|
||||
created_at: pod.created_at,
|
||||
service_url: format!(
|
||||
"http://{}:{}",
|
||||
pod.container_ip,
|
||||
shared_types::GRPC_DEFAULT_PORT
|
||||
),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Find container by project_id (returns None if not running)
|
||||
async fn find_container(
|
||||
&self,
|
||||
project_id: &str,
|
||||
service_type: &ServiceType,
|
||||
) -> ContainerRuntimeResult<Option<RuntimeContainerInfo>>;
|
||||
|
||||
/// Stop and remove container
|
||||
async fn stop_container(&self, project_id: &str) -> ContainerRuntimeResult<()>;
|
||||
|
||||
/// Stop and remove container by identifier + service type.
|
||||
///
|
||||
/// `identifier` means:
|
||||
/// - RCoder: `project_id`
|
||||
/// - ComputerAgentRunner: `user_id`
|
||||
async fn stop_container_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
_service_type: &ServiceType,
|
||||
) -> ContainerRuntimeResult<()> {
|
||||
self.stop_container(identifier).await
|
||||
}
|
||||
|
||||
/// Get container status
|
||||
async fn is_container_running(&self, project_id: &str) -> ContainerRuntimeResult<bool>;
|
||||
|
||||
/// Get container status by identifier + service type.
|
||||
async fn is_container_running_by_identifier(
|
||||
&self,
|
||||
identifier: &str,
|
||||
service_type: &ServiceType,
|
||||
) -> ContainerRuntimeResult<bool> {
|
||||
Ok(self
|
||||
.find_container(identifier, service_type)
|
||||
.await?
|
||||
.map(|c| c.status == ContainerRuntimeStatus::Running)
|
||||
.unwrap_or(false))
|
||||
}
|
||||
|
||||
/// List all containers managed by this runtime
|
||||
async fn list_containers(&self) -> ContainerRuntimeResult<Vec<RuntimeContainerInfo>>;
|
||||
|
||||
/// 同步缓存状态,清理失效的容器记录
|
||||
///
|
||||
/// 对于 Docker:遍历 ContainerStateActor 缓存,通过 Docker API 验证容器是否仍存在
|
||||
/// 对于 K8s:遍历 pod_cache,通过 K8s API 验证 Pod 是否仍存在
|
||||
///
|
||||
/// # Returns
|
||||
/// 返回元组 (已检查数量, 已移除容器信息列表)
|
||||
async fn sync_states(&self) -> ContainerRuntimeResult<(u32, Vec<RemovedContainerInfo>)> {
|
||||
// 默认实现:不做任何事(向后兼容)
|
||||
Ok((0, Vec::new()))
|
||||
}
|
||||
|
||||
/// Cleanup all containers (used on shutdown)
|
||||
async fn cleanup_all(&self) -> ContainerRuntimeResult<()>;
|
||||
|
||||
/// Health check - verify runtime is accessible
|
||||
async fn health_check(&self) -> ContainerRuntimeResult<()>;
|
||||
}
|
||||
Reference in New Issue
Block a user