sentinel_agent_protocol/v2/
control.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct CancelRequest {
8 pub correlation_id: String,
9 pub reason: CancelReason,
10 pub timestamp_ms: u64,
11}
12
13impl CancelRequest {
14 pub fn new(correlation_id: impl Into<String>, reason: CancelReason) -> Self {
15 Self {
16 correlation_id: correlation_id.into(),
17 reason,
18 timestamp_ms: now_ms(),
19 }
20 }
21
22 pub fn timeout(correlation_id: impl Into<String>) -> Self {
23 Self::new(correlation_id, CancelReason::Timeout)
24 }
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
29#[serde(rename_all = "snake_case", tag = "type")]
30pub enum CancelReason {
31 ClientDisconnect,
32 Timeout,
33 BlockedByAgent { agent_id: String },
34 UpstreamError,
35 ProxyShutdown,
36 Manual { reason: String },
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
41pub struct ConfigUpdateRequest {
42 pub update_type: ConfigUpdateType,
43 pub request_id: String,
44 pub timestamp_ms: u64,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
49#[serde(rename_all = "snake_case", tag = "type")]
50pub enum ConfigUpdateType {
51 RequestReload,
52 RuleUpdate { rule_set: String, rules: Vec<RuleDefinition>, remove_rules: Vec<String> },
53 ListUpdate { list_id: String, add: Vec<String>, remove: Vec<String> },
54 RestartRequired { reason: String, grace_period_ms: u64 },
55 ConfigError { error: String, field: Option<String> },
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
60pub struct RuleDefinition {
61 pub id: String,
62 pub priority: i32,
63 pub definition: serde_json::Value,
64 pub enabled: bool,
65 pub description: Option<String>,
66 #[serde(default)]
67 pub tags: Vec<String>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ConfigUpdateResponse {
73 pub request_id: String,
74 pub accepted: bool,
75 pub error: Option<String>,
76 pub timestamp_ms: u64,
77}
78
79impl ConfigUpdateResponse {
80 pub fn success(request_id: impl Into<String>) -> Self {
81 Self { request_id: request_id.into(), accepted: true, error: None, timestamp_ms: now_ms() }
82 }
83
84 pub fn failure(request_id: impl Into<String>, error: impl Into<String>) -> Self {
85 Self { request_id: request_id.into(), accepted: false, error: Some(error.into()), timestamp_ms: now_ms() }
86 }
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct ShutdownRequest {
92 pub reason: ShutdownReason,
93 pub grace_period_ms: u64,
94 pub timestamp_ms: u64,
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
99#[serde(rename_all = "snake_case")]
100pub enum ShutdownReason {
101 Graceful,
102 Immediate,
103 ConfigReload,
104 Upgrade,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct DrainRequest {
110 pub duration_ms: u64,
111 pub reason: DrainReason,
112 pub timestamp_ms: u64,
113}
114
115#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
117#[serde(rename_all = "snake_case")]
118pub enum DrainReason {
119 ConfigReload,
120 Maintenance,
121 HealthCheckFailed,
122 Manual,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct LogMessage {
128 pub level: LogLevel,
129 pub message: String,
130 pub correlation_id: Option<String>,
131 #[serde(default)]
132 pub fields: std::collections::HashMap<String, serde_json::Value>,
133 pub timestamp_ms: u64,
134}
135
136#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
138#[serde(rename_all = "lowercase")]
139pub enum LogLevel {
140 Debug,
141 Info,
142 Warn,
143 Error,
144}
145
146fn now_ms() -> u64 {
147 std::time::SystemTime::now()
148 .duration_since(std::time::UNIX_EPOCH)
149 .map(|d| d.as_millis() as u64)
150 .unwrap_or(0)
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_cancel_request() {
159 let cancel = CancelRequest::timeout("req-123");
160 assert_eq!(cancel.correlation_id, "req-123");
161 assert_eq!(cancel.reason, CancelReason::Timeout);
162 }
163
164 #[test]
165 fn test_config_update_response() {
166 let success = ConfigUpdateResponse::success("update-1");
167 assert!(success.accepted);
168
169 let failure = ConfigUpdateResponse::failure("update-2", "Error");
170 assert!(!failure.accepted);
171 }
172}