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 {
53 rule_set: String,
54 rules: Vec<RuleDefinition>,
55 remove_rules: Vec<String>,
56 },
57 ListUpdate {
58 list_id: String,
59 add: Vec<String>,
60 remove: Vec<String>,
61 },
62 RestartRequired {
63 reason: String,
64 grace_period_ms: u64,
65 },
66 ConfigError {
67 error: String,
68 field: Option<String>,
69 },
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
74pub struct RuleDefinition {
75 pub id: String,
76 pub priority: i32,
77 pub definition: serde_json::Value,
78 pub enabled: bool,
79 pub description: Option<String>,
80 #[serde(default)]
81 pub tags: Vec<String>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct ConfigUpdateResponse {
87 pub request_id: String,
88 pub accepted: bool,
89 pub error: Option<String>,
90 pub timestamp_ms: u64,
91}
92
93impl ConfigUpdateResponse {
94 pub fn success(request_id: impl Into<String>) -> Self {
95 Self {
96 request_id: request_id.into(),
97 accepted: true,
98 error: None,
99 timestamp_ms: now_ms(),
100 }
101 }
102
103 pub fn failure(request_id: impl Into<String>, error: impl Into<String>) -> Self {
104 Self {
105 request_id: request_id.into(),
106 accepted: false,
107 error: Some(error.into()),
108 timestamp_ms: now_ms(),
109 }
110 }
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct ShutdownRequest {
116 pub reason: ShutdownReason,
117 pub grace_period_ms: u64,
118 pub timestamp_ms: u64,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
123#[serde(rename_all = "snake_case")]
124pub enum ShutdownReason {
125 Graceful,
126 Immediate,
127 ConfigReload,
128 Upgrade,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct DrainRequest {
134 pub duration_ms: u64,
135 pub reason: DrainReason,
136 pub timestamp_ms: u64,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
141#[serde(rename_all = "snake_case")]
142pub enum DrainReason {
143 ConfigReload,
144 Maintenance,
145 HealthCheckFailed,
146 Manual,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct LogMessage {
152 pub level: LogLevel,
153 pub message: String,
154 pub correlation_id: Option<String>,
155 #[serde(default)]
156 pub fields: std::collections::HashMap<String, serde_json::Value>,
157 pub timestamp_ms: u64,
158}
159
160#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
162#[serde(rename_all = "lowercase")]
163pub enum LogLevel {
164 Debug,
165 Info,
166 Warn,
167 Error,
168}
169
170fn now_ms() -> u64 {
171 std::time::SystemTime::now()
172 .duration_since(std::time::UNIX_EPOCH)
173 .map(|d| d.as_millis() as u64)
174 .unwrap_or(0)
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_cancel_request() {
183 let cancel = CancelRequest::timeout("req-123");
184 assert_eq!(cancel.correlation_id, "req-123");
185 assert_eq!(cancel.reason, CancelReason::Timeout);
186 }
187
188 #[test]
189 fn test_config_update_response() {
190 let success = ConfigUpdateResponse::success("update-1");
191 assert!(success.accepted);
192
193 let failure = ConfigUpdateResponse::failure("update-2", "Error");
194 assert!(!failure.accepted);
195 }
196}