Repository navigation
Support kata container runtime - #84
Conversation
- Validate stack_id to prevent path traversal in marketplace deploy - Return proper HTTP status codes (502/504) on marketplace proxy errors - Replace blocking std::fs with tokio::fs in link_page and unlink_handler - Fix deployStack() to show stack name instead of ID in confirmation modal - Add rel="noopener noreferrer" to target="_blank" links Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds Kata Containers runtime selection support to the agent deploy flow, including capability discovery and compose runtime injection, alongside a couple of UI/security and async I/O improvements.
Changes:
- Introduce
ContainerRuntime(runc/kata) for deploy commands, inject runtime into compose YAML, and return effective runtime in deploy results. - Expose Kata support via
/capabilitieswhen detected. - UI/security tweaks: fix marketplace deploy modal naming, add
rel="noopener noreferrer"to external links, and harden marketplace deploystack_idvalidation; convert link/unlink filesystem ops to async.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| TODO.md | Documents intended “Kata Containers Support” follow-ups for Stacker/CLI/UI. |
| templates/marketplace.html | Uses the correct stack card to display the stack name in the deploy modal. |
| templates/link.html | Adds rel="noopener noreferrer" and updates the signup link URL. |
| src/comms/local_api.rs | Adds Kata feature detection in /capabilities, improves marketplace error status propagation, validates stack_id, and switches link/unlink file I/O to tokio FS. |
| src/commands/stacker.rs | Adds runtime enum + compose runtime injection + deploy output fields, and accompanying unit tests. |
| ); | ||
| } | ||
|
|
||
| #[cfg(test)] |
There was a problem hiding this comment.
runtime_compose_tests is only gated by #[cfg(test)], but it calls inject_runtime_into_compose, which is compiled only when feature = "docker" is enabled. CI runs cargo test --no-default-features --features minimal, so this module will fail to compile under the minimal feature set. Gate this test module with #[cfg(all(test, feature = "docker"))] (or provide a non-docker stub) so minimal builds/tests pass.
| #[cfg(test)] | |
| #[cfg(all(test, feature = "docker"))] |
| if *runtime == ContainerRuntime::Kata && std::path::Path::new(&compose_file).exists() { | ||
| let kata_available = detect_kata_runtime().await; | ||
| if kata_available { | ||
| if let Ok(existing) = tokio::fs::read_to_string(&compose_file).await { | ||
| let injected = inject_runtime_into_compose(&existing, runtime); | ||
| if let Err(e) = tokio::fs::write(&compose_file, &injected).await { | ||
| errors.push(make_error( | ||
| "runtime_inject_warning", | ||
| format!("Failed to inject Kata runtime into compose file: {}", e), | ||
| None, | ||
| )); | ||
| } else { | ||
| tracing::info!( | ||
| compose_file = %compose_file, | ||
| "Injected Kata runtime into existing compose file" | ||
| ); | ||
| } | ||
| } | ||
| } else { | ||
| tracing::warn!( | ||
| "Kata runtime requested but not available on this host — falling back to runc" | ||
| ); | ||
| errors.push(make_error( | ||
| "kata_fallback", | ||
| "Kata runtime requested but not available on this host; deploying with runc", | ||
| None, | ||
| )); |
There was a problem hiding this comment.
This async handler uses std::path::Path::exists() to check for the compose file. That call is blocking and can stall the Tokio runtime under load. Prefer tokio::fs::try_exists(...).await (or tokio::fs::metadata) so the runtime injection path remains non-blocking.
| if *runtime == ContainerRuntime::Kata && std::path::Path::new(&compose_file).exists() { | |
| let kata_available = detect_kata_runtime().await; | |
| if kata_available { | |
| if let Ok(existing) = tokio::fs::read_to_string(&compose_file).await { | |
| let injected = inject_runtime_into_compose(&existing, runtime); | |
| if let Err(e) = tokio::fs::write(&compose_file, &injected).await { | |
| errors.push(make_error( | |
| "runtime_inject_warning", | |
| format!("Failed to inject Kata runtime into compose file: {}", e), | |
| None, | |
| )); | |
| } else { | |
| tracing::info!( | |
| compose_file = %compose_file, | |
| "Injected Kata runtime into existing compose file" | |
| ); | |
| } | |
| } | |
| } else { | |
| tracing::warn!( | |
| "Kata runtime requested but not available on this host — falling back to runc" | |
| ); | |
| errors.push(make_error( | |
| "kata_fallback", | |
| "Kata runtime requested but not available on this host; deploying with runc", | |
| None, | |
| )); | |
| if *runtime == ContainerRuntime::Kata { | |
| match tokio::fs::try_exists(&compose_file).await { | |
| Ok(true) => { | |
| let kata_available = detect_kata_runtime().await; | |
| if kata_available { | |
| if let Ok(existing) = tokio::fs::read_to_string(&compose_file).await { | |
| let injected = inject_runtime_into_compose(&existing, runtime); | |
| if let Err(e) = tokio::fs::write(&compose_file, &injected).await { | |
| errors.push(make_error( | |
| "runtime_inject_warning", | |
| format!("Failed to inject Kata runtime into compose file: {}", e), | |
| None, | |
| )); | |
| } else { | |
| tracing::info!( | |
| compose_file = %compose_file, | |
| "Injected Kata runtime into existing compose file" | |
| ); | |
| } | |
| } | |
| } else { | |
| tracing::warn!( | |
| "Kata runtime requested but not available on this host — falling back to runc" | |
| ); | |
| errors.push(make_error( | |
| "kata_fallback", | |
| "Kata runtime requested but not available on this host; deploying with runc", | |
| None, | |
| )); | |
| } | |
| } | |
| Ok(false) => {} | |
| Err(e) => { | |
| errors.push(make_error( | |
| "runtime_inject_warning", | |
| format!("Failed to check compose file before runtime injection: {}", e), | |
| None, | |
| )); | |
| } |
| // Detect Kata Containers runtime availability | ||
| #[cfg(feature = "docker")] | ||
| { | ||
| if crate::commands::stacker::detect_kata_runtime().await { | ||
| features.push("kata".to_string()); | ||
| } |
There was a problem hiding this comment.
/capabilities shells out to docker info on every request via detect_kata_runtime(). If Docker is slow/unavailable, this can add noticeable latency or hang the endpoint (there’s no timeout in detect_kata_runtime). Consider caching the detection result (e.g., OnceLock + refresh interval) and/or wrapping the detection call in a short tokio::time::timeout so /capabilities remains responsive.
| let reg_path = "/etc/status-panel/registration.json"; | ||
| if tokio::fs::try_exists(reg_path).await.unwrap_or(false) { | ||
| if let Err(e) = tokio::fs::remove_file(reg_path).await { |
There was a problem hiding this comment.
try_exists(...).await.unwrap_or(false) suppresses any underlying IO error (e.g., permission issues) and silently behaves like “not linked”. Logging the error path would make unlink failures diagnosable (especially on read-only /etc or permission mismatches).
- Gate runtime_compose_tests with #[cfg(all(test, feature = "docker"))] - Replace blocking Path::exists() with tokio::fs::try_exists() in deploy - Cache detect_kata_runtime() with OnceLock + 5s timeout - Log errors in unlink_handler try_exists path - Bump version to 0.1.6 - Update CHANGELOG Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
No description provided.