添加qiming-rcoder模块
This commit is contained in:
325
qiming-rcoder/specs/k8s-runtime-support/tasks.md
Normal file
325
qiming-rcoder/specs/k8s-runtime-support/tasks.md
Normal file
@@ -0,0 +1,325 @@
|
||||
# Tasks: K8s Runtime Support Fix
|
||||
|
||||
**Branch**: `dev-k8s` | **Date**: 2026-04-20
|
||||
**Plan**: [plan.md](./plan.md) | **Spec**: [spec.md](./spec.md)
|
||||
|
||||
## Task List
|
||||
|
||||
### Phase 1: Critical Fixes (P0)
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Fix `global::init_global_docker_manager_with_config()` K8s path
|
||||
**File**: `crates/docker_manager/src/lib.rs`
|
||||
**Priority**: P0
|
||||
**Estimated Time**: 30 min
|
||||
**Status**: TODO
|
||||
|
||||
**Problem**: When `CONTAINER_RUNTIME=kubernetes`, the function calls `RuntimeManager::init()` but then still tries to set `GLOBAL_DOCKER_MANAGER` which fails silently or causes issues.
|
||||
|
||||
**Changes**:
|
||||
```rust
|
||||
#[cfg(feature = "kubernetes")]
|
||||
pub async fn init_global_docker_manager_with_config(
|
||||
config: DockerManagerConfig,
|
||||
) -> DockerResult<()> {
|
||||
let runtime_type = RuntimeType::from_env();
|
||||
crate::runtime::RuntimeManager::init(config.clone())
|
||||
.await
|
||||
.map_err(|e| DockerError::ConfigurationError(e.to_string()))?;
|
||||
info!("Runtime initialized with config");
|
||||
|
||||
if runtime_type == RuntimeType::Docker {
|
||||
let manager = Arc::new(DockerManager::new(config).await?);
|
||||
GLOBAL_DOCKER_MANAGER.set(manager).map_err(|_| {
|
||||
DockerError::IoError(std::io::Error::new(
|
||||
std::io::ErrorKind::AlreadyExists,
|
||||
"global DockerManager already initialized",
|
||||
))
|
||||
})?;
|
||||
info!("DockerManager initialized with config");
|
||||
}
|
||||
// K8s mode: RuntimeManager is initialized, GLOBAL_DOCKER_MANAGER stays empty (ok)
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
**Verification**:
|
||||
- [ ] `cargo check -p docker_manager --features kubernetes` passes
|
||||
- [ ] Unit test `test_runtime_type_from_env_kubernetes` passes
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Verify `main.rs` runtime initialization flow
|
||||
**File**: `crates/rcoder/src/main.rs`
|
||||
**Priority**: P0
|
||||
**Estimated Time**: 30 min
|
||||
**Status**: TODO
|
||||
|
||||
**Problem**: Need to verify that after calling `init_global_docker_manager_with_config()`, `RuntimeManager::get()` works correctly for K8s mode.
|
||||
|
||||
**Current Code** (lines 181-186):
|
||||
```rust
|
||||
if let Err(e) =
|
||||
docker_manager::global::init_global_docker_manager_with_config(docker_manager_config).await
|
||||
{
|
||||
error!("Docker Manager initializefailed: {}", e);
|
||||
return Err(anyhow::anyhow!("Docker Manager initialization failed: {}", e));
|
||||
}
|
||||
```
|
||||
|
||||
**Verification**:
|
||||
- [ ] K8s mode: `RuntimeManager::get().await` returns `Arc<dyn ContainerRuntime>`
|
||||
- [ ] Docker mode: `get_global_docker_manager().await` returns `Arc<DockerManager>`
|
||||
- [ ] Both modes can call `cleanup_all()` without error
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Fix `stop_container` in KubernetesRuntime to handle service_type
|
||||
**File**: `crates/docker_manager/src/runtime/kubernetes_runtime.rs`
|
||||
**Priority**: P0
|
||||
**Estimated Time**: 45 min
|
||||
**Status**: TODO
|
||||
|
||||
**Problem**: `stop_container(&self, project_id: &str)` only takes project_id, but K8s creates different pod types (RCoder vs ComputerAgentRunner) with different prefixes.
|
||||
|
||||
**Current**:
|
||||
```rust
|
||||
async fn stop_container(&self, project_id: &str) -> ContainerRuntimeResult<()> {
|
||||
let pod_name = self.pod_name(project_id, &ServiceType::RCoder); // Always RCoder!
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Fix Options**:
|
||||
|
||||
Option A - Add service_type parameter to `stop_container`:
|
||||
```rust
|
||||
async fn stop_container(&self, project_id: &str, service_type: ServiceType) -> ContainerRuntimeResult<()> {
|
||||
let pod_name = self.pod_name(project_id, &service_type);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
⚠️ This breaks the trait signature.
|
||||
|
||||
Option B - Cache service_type in pod_cache:
|
||||
```rust
|
||||
// Store (identifier, service_type) in cache when creating container
|
||||
self.pod_cache.write().await.insert(identifier.to_string(), (pod_info, service_type.clone()));
|
||||
|
||||
// In stop_container, lookup cached service_type or default to RCoder
|
||||
let service_type = self.get_cached_service_type(identifier).await.unwrap_or(ServiceType::RCoder);
|
||||
```
|
||||
|
||||
Option C - Use `stop_container_by_identifier` which already has service_type:
|
||||
```rust
|
||||
async fn stop_container(&self, project_id: &str) -> ContainerRuntimeResult<()> {
|
||||
// Try RCoder first, then ComputerAgentRunner
|
||||
match self.stop_container_by_identifier(project_id, &ServiceType::RCoder).await {
|
||||
Ok(()) => Ok(()),
|
||||
Err(ContainerRuntimeError::ContainerStopError(_)) => {
|
||||
// Try ComputerAgentRunner if RCoder not found
|
||||
self.stop_container_by_identifier(project_id, &ServiceType::ComputerAgentRunner).await
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
```
|
||||
✅ **Recommended** - Uses existing trait method, handles both types
|
||||
|
||||
**Implementation**: Use Option C
|
||||
|
||||
**Verification**:
|
||||
- [ ] `stop_container("user123")` correctly deletes ComputerAgentRunner pod when it exists
|
||||
- [ ] `stop_container("project123")` correctly deletes RCoder pod when it exists
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Fix Makefile k8s commands
|
||||
**File**: `Makefile`
|
||||
**Priority**: P1
|
||||
**Estimated Time**: 20 min
|
||||
**Status**: TODO
|
||||
|
||||
**Problem**:
|
||||
1. `dev-up-k8s` and `dev-restart-k8s` do `kubectl apply` followed by `kubectl set image`, but the image in deployment.yaml is hardcoded to `rcoder:test`
|
||||
2. `dev-restart-k8s` uses `rollout restart` which doesn't pick up the new image from `set image`
|
||||
|
||||
**Current (broken)**:
|
||||
```makefile
|
||||
dev-up-k8s:
|
||||
kubectl apply -f k8s/manifests/rcoder-deployment.yaml # Uses image: rcoder:test
|
||||
kubectl set image deployment/rcoder rcoder=$(IMAGE) # But this updates it
|
||||
...
|
||||
|
||||
dev-restart-k8s: dev-build-k8s
|
||||
kubectl apply -f k8s/manifests/rcoder-deployment.yaml
|
||||
kubectl set image deployment/rcoder rcoder=$(IMAGE) # Sets new image
|
||||
kubectl rollout restart deploy/rcoder # But restart uses deployment.yaml, not set image!
|
||||
```
|
||||
|
||||
**Fix**:
|
||||
```makefile
|
||||
# Use sed to replace image in deployment.yaml before applying
|
||||
define apply_with_image
|
||||
kubectl apply -f k8s/manifests/namespace.yaml
|
||||
kubectl apply -f k8s/manifests/serviceaccount.yaml
|
||||
sed "s|image: rcoder:test|image: $(IMAGE)|" k8s/manifests/rcoder-deployment.yaml | kubectl apply -f -
|
||||
kubectl apply -f k8s/manifests/rcoder-service.yaml
|
||||
endef
|
||||
|
||||
dev-up-k8s:
|
||||
$(call apply_with_image)
|
||||
kubectl rollout status deploy/rcoder -n $(K8S_NAMESPACE) --timeout=$(ROLLOUT_TIMEOUT)
|
||||
|
||||
dev-restart-k8s: dev-build-k8s
|
||||
kubectl delete pods -n $(K8S_NAMESPACE) -l app=rcoder --ignore-not-found
|
||||
kubectl rollout status deploy/rcoder -n $(K8S_NAMESPACE) --timeout=$(ROLLOUT_TIMEOUT)
|
||||
```
|
||||
|
||||
**Key changes**:
|
||||
1. Use `sed` to replace image tag at apply time
|
||||
2. `dev-restart-k8s` uses `kubectl delete pods` instead of `rollout restart` (simpler, faster)
|
||||
3. `dev-down-k8s` stays the same (already correct)
|
||||
|
||||
**Verification**:
|
||||
- [ ] `make dev-up-k8s IMAGE=rcoder:test-k8s` deploys with correct image
|
||||
- [ ] `make dev-restart-k8s IMAGE=rcoder:test-k8s` rebuilds and redeploys
|
||||
- [ ] `make dev-down-k8s` cleans up properly
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Add K8s mode to Cargo features in rcoder
|
||||
**File**: `crates/rcoder/Cargo.toml`
|
||||
**Priority**: P1
|
||||
**Estimated Time**: 10 min
|
||||
**Status**: TODO
|
||||
|
||||
**Problem**: The `kubernetes` feature in rcoder passes to docker_manager, but need to verify it's properly configured.
|
||||
|
||||
**Current**:
|
||||
```toml
|
||||
[features]
|
||||
# Kubernetes 支持:启用 Kubernetes 运行时模式
|
||||
# 启用后可通过 CONTAINER_RUNTIME=kubernetes 环境变量切换到 K8s 模式
|
||||
kubernetes = ["docker_manager/kubernetes"]
|
||||
```
|
||||
|
||||
**Verification**:
|
||||
- [ ] `cargo build --features kubernetes` works
|
||||
- [ ] `cargo build --features kubernetes --package rcoder` produces binary with K8s support
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Test K8s mode end-to-end
|
||||
**Priority**: P0
|
||||
**Estimated Time**: 60 min
|
||||
**Status**: TODO
|
||||
|
||||
**Prerequisites**: Kind cluster or real K8s cluster available
|
||||
|
||||
**Test Steps**:
|
||||
```bash
|
||||
# 1. Build K8s image
|
||||
make dev-build-k8s IMAGE=rcoder:test-k8s
|
||||
|
||||
# 2. Deploy to K8s
|
||||
make dev-up-k8s IMAGE=rcoder:test-k8s
|
||||
|
||||
# 3. Check rcoder pod is running
|
||||
kubectl get pods -n rcoder
|
||||
kubectl logs -n rcoder -l app=rcoder
|
||||
|
||||
# 4. Test /health endpoint
|
||||
NODE_PORT=$(kubectl get svc rcoder -n rcoder -o jsonpath='{.spec.ports[0].nodePort}')
|
||||
curl http://localhost:$NODE_PORT/health
|
||||
|
||||
# 5. Test /chat endpoint (creates RCoder agent pod)
|
||||
curl -X POST http://localhost:$NODE_PORT/chat \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"prompt": "hello"}'
|
||||
|
||||
# 6. Check agent pod was created
|
||||
kubectl get pods -n rcoder
|
||||
|
||||
# 7. Test /computer/chat endpoint (creates ComputerAgentRunner pod)
|
||||
curl -X POST http://localhost:$NODE_PORT/computer/chat \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"user_id": "test-user", "prompt": "hello"}'
|
||||
|
||||
# 8. Check computer agent pod was created
|
||||
kubectl get pods -n rcoder -l user_id=test-user
|
||||
|
||||
# 9. Test cleanup (delete chat session)
|
||||
# ... verify pods are deleted
|
||||
|
||||
# 10. Cleanup
|
||||
make dev-down-k8s
|
||||
```
|
||||
|
||||
**Expected Results**:
|
||||
- [ ] `/health` returns healthy
|
||||
- [ ] `/chat` creates `rcoder-agent-{project_id}` pod
|
||||
- [ ] `/computer/chat` creates `computer-agent-runner-{user_id}` pod
|
||||
- [ ] Both pods are reachable via gRPC from rcoder main pod
|
||||
- [ ] Cleanup properly deletes pods
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Verify Docker mode still works (regression test)
|
||||
**Priority**: P1
|
||||
**Estimated Time**: 30 min
|
||||
**Status**: TODO
|
||||
|
||||
**Test Steps**:
|
||||
```bash
|
||||
# 1. Build Docker image
|
||||
make dev-build
|
||||
|
||||
# 2. Start Docker Compose
|
||||
make dev-up
|
||||
|
||||
# 3. Test /health
|
||||
curl http://localhost:8087/health
|
||||
|
||||
# 4. Test /chat
|
||||
curl -X POST http://localhost:8087/chat \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"prompt": "hello"}'
|
||||
|
||||
# 5. Verify container created
|
||||
docker ps | grep rcoder-agent
|
||||
|
||||
# 6. Cleanup
|
||||
make dev-down
|
||||
```
|
||||
|
||||
**Expected Results**:
|
||||
- [ ] All existing functionality works as before
|
||||
- [ ] No regression in Docker mode
|
||||
|
||||
---
|
||||
|
||||
## Task Summary
|
||||
|
||||
| # | Task | Priority | Status | Dependencies |
|
||||
|---|------|----------|--------|--------------|
|
||||
| 1 | Fix `init_global_docker_manager_with_config()` | P0 | TODO | - |
|
||||
| 2 | Verify `main.rs` runtime init | P0 | TODO | Task 1 |
|
||||
| 3 | Fix `stop_container` service_type | P0 | TODO | - |
|
||||
| 4 | Fix Makefile k8s commands | P1 | TODO | - |
|
||||
| 5 | Verify Cargo features | P1 | TODO | - |
|
||||
| 6 | Test K8s mode end-to-end | P0 | TODO | Tasks 1-5 |
|
||||
| 7 | Regression test Docker mode | P1 | TODO | - |
|
||||
|
||||
---
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
- [ ] All P0 tasks complete
|
||||
- [ ] K8s mode can create/manage RCoder agent pods
|
||||
- [ ] K8s mode can create/manage ComputerAgentRunner pods
|
||||
- [ ] Docker mode regression tests pass
|
||||
- [ ] Makefile k8s commands work correctly
|
||||
- [ ] Code compiles with both `kubernetes` feature enabled and disabled
|
||||
Reference in New Issue
Block a user