microsandbox_control_client/
compat_message.rs1use microsandbox_protocol::control::{ControlRequest, SecretChange, SecretValue, SecretsResult};
4use microsandbox_protocol_client::{
5 ClientError, EncodedMessage, ErrorKind, IntoOutboundMessage, Request, TypedMessage,
6};
7use serde::Serialize;
8use zeroize::Zeroizing;
9
10use crate::{
11 ControlClientError, ControlClientResult, ControlProtocol, GetCapabilities, GetCpuState,
12 GetMemoryState, JsonReply, JsonValue, SetCpuTarget, SetMemoryTarget, UpdateSecrets, json_reply,
13};
14
15pub trait IntoControlMessage: IntoOutboundMessage<ControlProtocol> {
21 fn into_json(self) -> ControlClientResult<ControlRequest>;
24}
25
26pub trait CheckedControlRequest: Request<ControlProtocol, Error = ControlClientError> {
28 fn json_request(&self) -> ControlClientResult<ControlRequest>;
30 fn decode_json(&self, reply: JsonReply) -> ControlClientResult<Self::Response>;
32}
33
34impl<T: Serialize> IntoControlMessage for TypedMessage<T> {
39 fn into_json(self) -> ControlClientResult<ControlRequest> {
40 let bytes = Zeroizing::new(
44 serde_json::to_vec(&self.payload).map_err(|_| ClientError::new(ErrorKind::Encode))?,
45 );
46 let value =
47 JsonValue::parse(&bytes).map_err(|_| ClientError::new(ErrorKind::InvalidData))?;
48 if value.as_object().is_none() {
49 return invalid();
50 }
51 Ok(match self.message_type.as_str() {
52 "control.capabilities" => ControlRequest::Capabilities,
53 "control.memory.state" => ControlRequest::MemoryState,
54 "control.cpu.state" => ControlRequest::CpuState,
55 "control.memory.target" => ControlRequest::MemoryTarget {
56 total_mib: value
57 .get("total_mib")
58 .and_then(JsonValue::as_u64)
59 .ok_or_else(invalid_error)?,
60 },
61 "control.cpu.target" => ControlRequest::CpuTarget {
62 online: value
63 .get("online")
64 .and_then(JsonValue::as_u64)
65 .and_then(|number| number.try_into().ok())
66 .ok_or_else(invalid_error)?,
67 },
68 "control.secrets.update" => ControlRequest::SecretsUpdate {
69 changes: value
70 .get("changes")
71 .and_then(JsonValue::as_array)
72 .ok_or_else(invalid_error)?
73 .iter()
74 .map(secret_change)
75 .collect::<ControlClientResult<_>>()?,
76 },
77 _ => return Err(ControlClientError::UnsupportedMode),
78 })
79 }
80}
81
82impl IntoControlMessage for EncodedMessage {
83 fn into_json(self) -> ControlClientResult<ControlRequest> {
84 Err(ControlClientError::UnsupportedMode)
85 }
86}
87
88impl CheckedControlRequest for GetCapabilities {
89 fn json_request(&self) -> ControlClientResult<ControlRequest> {
90 Ok(ControlRequest::Capabilities)
91 }
92 fn decode_json(&self, reply: JsonReply) -> ControlClientResult<Self::Response> {
93 reply.checked(|value| json_reply::capabilities(value.get("capabilities")?))
94 }
95}
96
97impl CheckedControlRequest for GetMemoryState {
98 fn json_request(&self) -> ControlClientResult<ControlRequest> {
99 Ok(ControlRequest::MemoryState)
100 }
101 fn decode_json(&self, reply: JsonReply) -> ControlClientResult<Self::Response> {
102 reply.checked(|value| json_reply::memory(value.get("memory")?))
103 }
104}
105
106impl CheckedControlRequest for SetMemoryTarget {
107 fn json_request(&self) -> ControlClientResult<ControlRequest> {
108 Ok(ControlRequest::MemoryTarget {
109 total_mib: self.total_mib,
110 })
111 }
112 fn decode_json(&self, reply: JsonReply) -> ControlClientResult<Self::Response> {
113 GetMemoryState.decode_json(reply)
114 }
115}
116
117impl CheckedControlRequest for GetCpuState {
118 fn json_request(&self) -> ControlClientResult<ControlRequest> {
119 Ok(ControlRequest::CpuState)
120 }
121 fn decode_json(&self, reply: JsonReply) -> ControlClientResult<Self::Response> {
122 reply.checked(|value| json_reply::cpu(value.get("cpu")?))
123 }
124}
125
126impl CheckedControlRequest for SetCpuTarget {
127 fn json_request(&self) -> ControlClientResult<ControlRequest> {
128 Ok(ControlRequest::CpuTarget {
129 online: self.online,
130 })
131 }
132 fn decode_json(&self, reply: JsonReply) -> ControlClientResult<Self::Response> {
133 GetCpuState.decode_json(reply)
134 }
135}
136
137impl CheckedControlRequest for UpdateSecrets {
138 fn json_request(&self) -> ControlClientResult<ControlRequest> {
139 Ok(ControlRequest::SecretsUpdate {
140 changes: self.changes.clone(),
141 })
142 }
143 fn decode_json(&self, reply: JsonReply) -> ControlClientResult<Self::Response> {
144 reply.checked(|_| {
145 Some(SecretsResult::Complete {
146 applied_count: self.changes.len().try_into().ok()?,
147 })
148 })
149 }
150}
151
152fn invalid_error() -> ControlClientError {
157 ClientError::new(ErrorKind::InvalidData).into()
158}
159
160fn invalid<T>() -> ControlClientResult<T> {
161 Err(invalid_error())
162}
163
164fn secret_change(value: &JsonValue) -> ControlClientResult<SecretChange> {
165 let name = value
166 .get("name")
167 .and_then(JsonValue::as_str)
168 .ok_or_else(invalid_error)?
169 .to_owned();
170 Ok(match value.get("change").and_then(JsonValue::as_str) {
171 Some("rotate") => SecretChange::Rotate {
172 name,
173 value: SecretValue(
174 value
175 .get("value")
176 .and_then(JsonValue::as_str)
177 .ok_or_else(invalid_error)?
178 .to_owned(),
179 ),
180 },
181 Some("remove") => SecretChange::Remove { name },
182 Some("set_allowed_hosts") => SecretChange::SetAllowedHosts {
183 name,
184 hosts: value
185 .get("hosts")
186 .and_then(JsonValue::as_array)
187 .ok_or_else(invalid_error)?
188 .iter()
189 .map(|host| host.as_str().map(str::to_owned).ok_or_else(invalid_error))
190 .collect::<ControlClientResult<_>>()?,
191 },
192 _ => return invalid(),
193 })
194}