WP-12: Kubernetes overview and workload actions
kube-rs gateway (nodes, namespaces, deployments/statefulsets/daemonsets with images, PVCs; rollout restart, scale, set image via patches) using the microk8s kubeconfig by default, fake gateway for dev, /api/cluster routes, Kubernetes page with node cards, workload table with admin actions and PVC list. Also implements the streaming command runner that WP-11 relied on. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@ -43,11 +43,52 @@ impl CommandRunner for SystemCommandRunner {
|
||||
|
||||
async fn run_streaming(
|
||||
&self,
|
||||
_program: &str,
|
||||
_args: &[&str],
|
||||
_out: &dyn LineSink,
|
||||
program: &str,
|
||||
args: &[&str],
|
||||
out: &dyn LineSink,
|
||||
) -> Result<bool, DomainError> {
|
||||
todo!()
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
let mut child = tokio::process::Command::new(program)
|
||||
.args(args)
|
||||
.env("DEBIAN_FRONTEND", "noninteractive")
|
||||
.env("LC_ALL", "C")
|
||||
.stdin(std::process::Stdio::null())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|e| DomainError::Unavailable(format!("{program}: {e}")))?;
|
||||
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<String>();
|
||||
let mut readers = Vec::new();
|
||||
if let Some(stdout) = child.stdout.take() {
|
||||
let tx = tx.clone();
|
||||
readers.push(tokio::spawn(async move {
|
||||
let mut lines = BufReader::new(stdout).lines();
|
||||
while let Ok(Some(l)) = lines.next_line().await {
|
||||
let _ = tx.send(l);
|
||||
}
|
||||
}));
|
||||
}
|
||||
if let Some(stderr) = child.stderr.take() {
|
||||
let tx = tx.clone();
|
||||
readers.push(tokio::spawn(async move {
|
||||
let mut lines = BufReader::new(stderr).lines();
|
||||
while let Ok(Some(l)) = lines.next_line().await {
|
||||
let _ = tx.send(l);
|
||||
}
|
||||
}));
|
||||
}
|
||||
drop(tx);
|
||||
while let Some(line) = rx.recv().await {
|
||||
out.line(&line);
|
||||
}
|
||||
for r in readers {
|
||||
let _ = r.await;
|
||||
}
|
||||
let status = child
|
||||
.wait()
|
||||
.await
|
||||
.map_err(|e| DomainError::Unavailable(format!("{program}: {e}")))?;
|
||||
Ok(status.success())
|
||||
}
|
||||
|
||||
async fn read_file(&self, path: &str) -> Result<Option<String>, DomainError> {
|
||||
|
||||
Reference in New Issue
Block a user