tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 83JSON 198635 Definitions

⎇ 94ea0eaa539cf93d58167ea1687b5a242b196573 crates/apps/sandokan/src/main.rs Rust · 125 lines · 4.0 KB History Who touched what raw

feat(sandokan): CLI de prueba + fix wire serialization 94ea0eaa5 · sergio ·

1//! `sandokan` — CLI de prueba del orquestador.
2//!
3//! Uso típico (dos terminales):
4//!   terminal 1:  sandokan daemon
5//!   terminal 2:  sandokan run /bin/sleep 300
6//!                sandokan list
7//!                sandokan status <card-id>
8//!                sandokan stop <card-id>
9//!
10//! Sin daemon, `run` igual encarna el proceso, pero el registro vive en
11//! el proceso del CLI y se pierde al salir — `list` no lo verá. Para
12//! probar el lifecycle completo, corré el daemon primero.
13
14use std::path::PathBuf;
15use std::sync::Arc;
16use std::time::Duration;
17
18use brahman_card::{Card, Payload};
19use clap::{Parser, Subcommand};
20use sandokan::{auto, default_socket_path, serve, Intent, LocalEngine};
21use ulid::Ulid;
22
23#[derive(Parser)]
24#[command(name = "sandokan", about = "Orquestador brahman — CLI de prueba")]
25struct Cli {
26    /// Socket del daemon (default: $XDG_RUNTIME_DIR/sandokan.sock).
27    #[arg(long, global = true)]
28    socket: Option<PathBuf>,
29    #[command(subcommand)]
30    cmd: Cmd,
31}
32
33#[derive(Subcommand)]
34enum Cmd {
35    /// Corre el daemon: sirve un LocalEngine sobre el socket.
36    Daemon,
37    /// Encarna un ejecutable como Card y lo orquesta.
38    Run {
39        /// Ruta del ejecutable.
40        exec: String,
41        /// Argumentos del ejecutable.
42        #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
43        args: Vec<String>,
44    },
45    /// Lista las entidades activas.
46    List,
47    /// Estado de una entidad.
48    Status { card_id: String },
49    /// Telemetría puntual de una entidad.
50    Telemetry { card_id: String },
51    /// Detiene una entidad (SIGTERM + gracia + SIGKILL).
52    Stop {
53        card_id: String,
54        #[arg(long, default_value = "1000")]
55        grace_ms: u64,
56    },
57}
58
59#[tokio::main]
60async fn main() -> anyhow::Result<()> {
61    let cli = Cli::parse();
62    let socket = cli.socket.unwrap_or_else(default_socket_path);
63
64    match cli.cmd {
65        Cmd::Daemon => {
66            if let Some(parent) = socket.parent() {
67                let _ = std::fs::create_dir_all(parent);
68            }
69            println!("sandokan-daemon escuchando en {}", socket.display());
70            println!("(Ctrl-C para salir)");
71            let engine = Arc::new(LocalEngine::new());
72            serve(engine, &socket).await?;
73        }
74        Cmd::Run { exec, args } => {
75            let mut card = Card::new(format!("run:{exec}"));
76            card.payload = Payload::Native {
77                exec,
78                argv: args,
79                envp: vec![],
80            };
81            let engine = auto(&socket).await;
82            let handle = engine.run(Intent::new(card)).await?;
83            println!("encarnado:");
84            println!("  card_id : {}", handle.card_id);
85            println!("  label   : {}", handle.label);
86        }
87        Cmd::List => {
88            let engine = auto(&socket).await;
89            let list = engine.list().await?;
90            if list.is_empty() {
91                println!("(sin entidades activas)");
92            }
93            for h in list {
94                println!("{}  {}", h.card_id, h.label);
95            }
96        }
97        Cmd::Status { card_id } => {
98            let engine = auto(&socket).await;
99            let state = engine.status(parse_id(&card_id)?).await?;
100            println!("{state:?}");
101        }
102        Cmd::Telemetry { card_id } => {
103            let engine = auto(&socket).await;
104            let t = engine.telemetry(parse_id(&card_id)?).await?;
105            println!(
106                "mem={} KiB  nproc={}  cpu={:.1}%",
107                t.mem_bytes / 1024,
108                t.nproc,
109                t.cpu_pct
110            );
111        }
112        Cmd::Stop { card_id, grace_ms } => {
113            let engine = auto(&socket).await;
114            engine
115                .stop(parse_id(&card_id)?, Duration::from_millis(grace_ms))
116                .await?;
117            println!("detenido");
118        }
119    }
120    Ok(())
121}
122
123fn parse_id(s: &str) -> anyhow::Result<Ulid> {
124    Ulid::from_string(s).map_err(|e| anyhow::anyhow!("card-id inválido `{s}`: {e}"))
125}