feat(shipote): data plane + DAG fan-in/out + stats + lifecycle (fases F-I)
Pipeline runtime:
- Fan-out 1→N (splitter task replica al N consumers) y fan-in N→1 (merger
task con mpsc + reader-per-input). DAGs no lineales soportados.
- Flow channels: Unix socket + tokio broadcast con replay buffer
configurable por pipeline (DiscernPolicy.replay_chunks). Subscribers
externos vía `shipote flow tail <socket>`.
- Templating en specs con `${KEY}` (CLI `--var KEY=VALUE`). Walk
recursivo sobre serde_json::Value, soporta todos los strings del schema.
- Pipelines guardados (`pipeline save/saved-list/drop/run-saved`)
persisten con el snapshot.
Lifecycle de comandos:
- Log capture per-stream (stdout/stderr separados) via pipe O_CLOEXEC +
AsyncFd. CLI `shipote logs <ws> <cmd> --stream {stdout,stderr,both}`.
- Stop graceful con tiempo configurable: SIGTERM → grace → SIGKILL.
Tanto a nivel workspace como pipeline individual.
- TTL auto-stop ya existente (Fase C) sigue funcionando.
ente-incarnate:
- ChildStdio declarativo (Fase C) + ChildPreExec declarativo nuevo:
NoNewPrivs, ParentDeathSig, Dumpable, NewSession, Chdir, Umask.
- Aplicación pre-execve async-signal-safe en ambos paths (plain via
Command::pre_exec, namespaced via callback del clone(2)).
Observabilidad:
- WorkspaceStats: RSS + RSS peak (VmHWM o memory.peak cgroup) + CPU usec
+ uptime. Fuente per-proc o cgroup según delegation.
- shipote-shell con sparkline ASCII por workspace (history cap 24),
card de flow channels activos, vista de comandos + saved pipelines.
- Tap → broker: cada edge enriquecido con TypeRef se anuncia como Card
efímera vía SidecarPool (graceful si broker no corre).
Discern:
- Integrado en yahweh-provider-fs (mime_type en EntityNode).
- Integrado en nouser-core::cluster::pick_lens como fallback cuando la
extensión cae a Lens::Grid.
79 tests pasan: ente-incarnate (16), nouser-core (27), shipote-card (8),
shipote-core (20), shipote-discern (5), yahweh-provider-fs (3).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,10 @@ use ulid::Ulid;
|
||||
pub const DEFAULT_SOCK_NAME: &str = "shipote.sock";
|
||||
pub const MAX_FRAME: usize = 1 << 20;
|
||||
|
||||
fn default_grace_ms() -> u64 {
|
||||
1000
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Mensajes
|
||||
// =====================================================================
|
||||
@@ -32,8 +36,13 @@ pub enum Request {
|
||||
/// Listar todos los workspaces vivos.
|
||||
WorkspaceList,
|
||||
|
||||
/// Detener un workspace y reapear sus comandos.
|
||||
WorkspaceStop { id: WorkspaceId },
|
||||
/// Detener un workspace y reapear sus comandos. `grace_ms`: tiempo
|
||||
/// que se espera tras SIGTERM antes de SIGKILL. 0 = SIGKILL inmediato.
|
||||
WorkspaceStop {
|
||||
id: WorkspaceId,
|
||||
#[serde(default = "default_grace_ms")]
|
||||
grace_ms: u64,
|
||||
},
|
||||
|
||||
/// Ejecutar un comando one-shot dentro de un workspace existente.
|
||||
Run {
|
||||
@@ -50,6 +59,10 @@ pub enum Request {
|
||||
/// consumidor de cada FlowEdge, sampleando los primeros bytes
|
||||
/// y discerniendo el TypeRef.
|
||||
tap: bool,
|
||||
/// Variables para sustitución `${KEY}` en strings del spec
|
||||
/// antes de spawn (templating).
|
||||
#[serde(default)]
|
||||
vars: std::collections::BTreeMap<String, String>,
|
||||
},
|
||||
|
||||
/// Discernir un buffer ad-hoc (sin workspace). Útil para `shipote discern <file>`.
|
||||
@@ -66,6 +79,8 @@ pub enum Request {
|
||||
workspace: shipote_card::WorkspaceId,
|
||||
command: Ulid,
|
||||
tail_bytes: usize,
|
||||
/// "stdout" | "stderr" | "both" (default "both" si vacío).
|
||||
stream: String,
|
||||
},
|
||||
|
||||
/// Guardar (o reemplazar) un PipelineSpec bajo un nombre.
|
||||
@@ -78,7 +93,29 @@ pub enum Request {
|
||||
PipelineDrop { name: String },
|
||||
|
||||
/// Ejecutar un pipeline guardado.
|
||||
PipelineRunSaved { name: String, tap: bool },
|
||||
PipelineRunSaved {
|
||||
name: String,
|
||||
tap: bool,
|
||||
#[serde(default)]
|
||||
vars: std::collections::BTreeMap<String, String>,
|
||||
},
|
||||
|
||||
/// Resource accounting de un workspace.
|
||||
WorkspaceStats { workspace: shipote_card::WorkspaceId },
|
||||
|
||||
/// Detener selectivamente los comandos de un pipeline (no el workspace
|
||||
/// entero). `grace_ms`: SIGTERM → wait → SIGKILL.
|
||||
PipelineStop {
|
||||
pipeline: Ulid,
|
||||
#[serde(default = "default_grace_ms")]
|
||||
grace_ms: u64,
|
||||
},
|
||||
|
||||
/// Listar pipelines activos con sus flow channels (data plane).
|
||||
FlowList,
|
||||
|
||||
/// Cerrar el data plane de un pipeline (drop sockets + canales).
|
||||
FlowDrop { pipeline: Ulid },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -148,11 +185,47 @@ pub enum Response {
|
||||
existed: bool,
|
||||
},
|
||||
|
||||
PipelineStopped {
|
||||
pipeline: Ulid,
|
||||
reaped: u32,
|
||||
},
|
||||
|
||||
WorkspaceStats {
|
||||
info: WorkspaceStatsInfo,
|
||||
},
|
||||
|
||||
FlowList {
|
||||
items: Vec<FlowInfo>,
|
||||
},
|
||||
|
||||
FlowDropped {
|
||||
pipeline: Ulid,
|
||||
existed: bool,
|
||||
},
|
||||
|
||||
Error {
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct WorkspaceStatsInfo {
|
||||
pub commands_alive: u32,
|
||||
pub commands_total: u32,
|
||||
pub rss_bytes: Option<u64>,
|
||||
#[serde(default)]
|
||||
pub rss_peak_bytes: Option<u64>,
|
||||
pub cpu_usec: Option<u64>,
|
||||
pub source: String,
|
||||
pub uptime_ms: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FlowInfo {
|
||||
pub pipeline: Ulid,
|
||||
pub sockets: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CommandInfo {
|
||||
pub id: Ulid,
|
||||
@@ -175,6 +248,9 @@ pub struct EdgeDiscernmentInfo {
|
||||
pub mime: Option<String>,
|
||||
pub lens: Option<String>,
|
||||
pub confidence: f32,
|
||||
/// Path del Unix socket donde otros módulos pueden suscribirse a los
|
||||
/// bytes replicados de este edge (data plane). `None` si tap=false.
|
||||
pub flow_socket: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user