[Fix] IPC, cli, daemon
This commit is contained in:
parent
36a70e82a0
commit
56aad3a023
32 changed files with 1356 additions and 399 deletions
|
|
@ -1,5 +1,14 @@
|
|||
pub mod protocol;
|
||||
pub mod transport;
|
||||
|
||||
pub use protocol::{ClientMessage, DaemonMessage, LogEntry, StateSnapshot};
|
||||
pub use protocol::{
|
||||
ClientMessage, DaemonMessage, HelloAck, LogEntry, StateSnapshot, MetricSample,
|
||||
RequestEnvelope, ResponseEnvelope, ResponseResult, LocalRequest, IpcErrorCode,
|
||||
ConnectionStatus, StartupPhase, LifecycleEvent,
|
||||
};
|
||||
pub use transport::{read_msg, write_msg};
|
||||
|
||||
/// Current IPC protocol version.
|
||||
pub const PROTOCOL_VERSION: u16 = 2;
|
||||
/// Minimum protocol version this daemon understands.
|
||||
pub const MIN_PROTOCOL_VERSION: u16 = 2;
|
||||
|
|
|
|||
|
|
@ -1,28 +1,123 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Client → Daemon
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum ClientMessage {
|
||||
Command { seq: u64, line: String },
|
||||
Subscribe,
|
||||
Hello { supported_versions: Vec<u16> },
|
||||
Subscribe { log_classes: Vec<String>, metric_interval_ms: Option<u64> },
|
||||
Request(RequestEnvelope),
|
||||
Ping { seq: u64 },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct RequestEnvelope {
|
||||
pub request_id: u64,
|
||||
pub protocol_version: u16,
|
||||
pub request: LocalRequest,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum LocalRequest {
|
||||
GetStatus,
|
||||
ListTasks,
|
||||
ListUsers,
|
||||
CreateUser { username: String },
|
||||
RemoveUser { user_id: i64 },
|
||||
ReconnectOmikron,
|
||||
RotateIotaIdentity,
|
||||
RestartDaemon,
|
||||
StopDaemon,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Daemon → Client
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum DaemonMessage {
|
||||
HelloAck(HelloAck),
|
||||
LogEntry(LogEntry),
|
||||
StateUpdate(StateSnapshot),
|
||||
CommandResult {
|
||||
seq: u64,
|
||||
success: bool,
|
||||
message: String,
|
||||
},
|
||||
Pong {
|
||||
seq: u64,
|
||||
},
|
||||
MetricSample(MetricSample),
|
||||
Response(ResponseEnvelope),
|
||||
Pong { seq: u64 },
|
||||
LifecycleEvent(LifecycleEvent),
|
||||
Gap { skipped: u64 },
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct HelloAck {
|
||||
pub protocol_version: u16,
|
||||
pub daemon_version: String,
|
||||
pub instance_id: String,
|
||||
pub startup_phase: StartupPhase,
|
||||
pub capabilities: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct ResponseEnvelope {
|
||||
pub request_id: u64,
|
||||
pub result: ResponseResult,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum ResponseResult {
|
||||
Ok(String),
|
||||
Error(IpcErrorCode),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum IpcErrorCode {
|
||||
InvalidRequest,
|
||||
NotFound,
|
||||
Conflict,
|
||||
StorageFailure,
|
||||
OmikronUnavailable,
|
||||
UnsupportedVersion,
|
||||
NotReady,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "type", content = "data", rename_all = "snake_case")]
|
||||
pub enum LifecycleEvent {
|
||||
StateChanged(ConnectionStatus),
|
||||
Shutdown { reason: String },
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum ConnectionStatus {
|
||||
Connected,
|
||||
Reconnecting,
|
||||
Degraded,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum StartupPhase {
|
||||
Starting,
|
||||
MigratingStorage,
|
||||
LoadingUsers,
|
||||
StartingServices,
|
||||
Ready,
|
||||
Degraded,
|
||||
Stopping,
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Shared types
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct LogEntry {
|
||||
pub timestamp_ms: u128,
|
||||
|
|
@ -40,3 +135,12 @@ pub struct StateSnapshot {
|
|||
pub net_down: Vec<(f64, f64)>,
|
||||
pub sys_info: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
||||
pub struct MetricSample {
|
||||
pub cpu: Option<f64>,
|
||||
pub ram: Option<f64>,
|
||||
pub ping: Option<f64>,
|
||||
pub net_up: Option<f64>,
|
||||
pub net_down: Option<f64>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,19 +41,20 @@ where
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{read_msg, write_msg};
|
||||
use crate::protocol::ClientMessage;
|
||||
use crate::protocol::{ClientMessage, LocalRequest, RequestEnvelope};
|
||||
|
||||
#[tokio::test]
|
||||
async fn round_trips_framed_messages() {
|
||||
let (mut writer, mut reader) = tokio::io::duplex(1024);
|
||||
let message = ClientMessage::Command {
|
||||
seq: 4,
|
||||
line: "help".into(),
|
||||
};
|
||||
let message = ClientMessage::Request(RequestEnvelope {
|
||||
request_id: 4,
|
||||
protocol_version: 2,
|
||||
request: LocalRequest::GetStatus,
|
||||
});
|
||||
write_msg(&mut writer, &message)
|
||||
.await
|
||||
.expect("write succeeds");
|
||||
let received: ClientMessage = read_msg(&mut reader).await.expect("read succeeds");
|
||||
assert!(matches!(received, ClientMessage::Command { seq: 4, line } if line == "help"));
|
||||
assert!(matches!(received, ClientMessage::Request(req) if req.request_id == 4));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue