Files
qiming/qiming-rcoder/crates/container-runtime-api/src/runtime_trait.rs
2026-06-01 13:54:52 +08:00

306 lines
9.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 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<()>;
}