plane_common/
protocol.rs

1use std::fmt::Display;
2
3use crate::{
4    log_types::{BackendAddr, LoggableTime},
5    names::{BackendActionName, BackendName},
6    typed_socket::ChannelMessage,
7    types::{
8        backend_state::TerminationReason, BackendState, BearerToken, ClusterName, KeyConfig,
9        NodeId, SecretToken, Subdomain, TerminationKind,
10    },
11};
12use serde::{Deserialize, Serialize};
13use serde_json::Value;
14
15#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
16pub enum ApiErrorKind {
17    FailedToAcquireKey,
18    KeyUnheldNoSpawnConfig,
19    KeyHeldUnhealthy,
20    KeyHeld,
21    NoDroneAvailable,
22    FailedToRemoveKey,
23    DatabaseError,
24    NoClusterProvided,
25    NotFound,
26    InvalidClusterName,
27    Other,
28}
29
30#[derive(thiserror::Error, Debug, Serialize, Deserialize)]
31pub struct ApiError {
32    pub id: String,
33    pub kind: ApiErrorKind,
34    pub message: String,
35}
36
37impl Display for ApiError {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        write!(f, "{:?}", self)
40    }
41}
42
43#[derive(Serialize, Deserialize, Debug, Clone, valuable::Valuable, PartialEq)]
44pub struct KeyDeadlines {
45    /// When the key should be renewed.
46    pub renew_at: LoggableTime,
47
48    /// When the backend should be soft-terminated if the key could not be renewed.
49    pub soft_terminate_at: LoggableTime,
50
51    /// When the backend should be hard-terminated if the key could not be renewed.
52    pub hard_terminate_at: LoggableTime,
53}
54
55#[derive(Serialize, Deserialize, Debug, Clone, valuable::Valuable, PartialEq)]
56pub struct AcquiredKey {
57    /// Details of the key itself.
58    pub key: KeyConfig,
59
60    /// Deadlines for key expiration stages.
61    pub deadlines: KeyDeadlines,
62
63    /// A unique key associated with a key for the duration it is acquired. This does not
64    /// change across renewals, but is incremented when the key is released and then acquired.
65    /// This is used internally to track the key during renewals, but can also be exposed to
66    /// backends as a fencing token.
67    /// (https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html).
68    pub token: i64,
69}
70
71#[derive(Serialize, Deserialize, Debug, Clone)]
72pub enum BackendAction {
73    Spawn {
74        executable: Value,
75        key: AcquiredKey,
76        static_token: Option<BearerToken>,
77    },
78    Terminate {
79        kind: TerminationKind,
80        reason: TerminationReason,
81    },
82}
83
84impl valuable::Valuable for BackendAction {
85    fn as_value(&self) -> valuable::Value {
86        valuable::Value::Mappable(self)
87    }
88
89    fn visit(&self, visit: &mut dyn valuable::Visit) {
90        match self {
91            BackendAction::Spawn {
92                key, static_token, ..
93            } => {
94                visit.visit_entry(valuable::Value::String("key"), key.as_value());
95                visit.visit_entry(
96                    valuable::Value::String("static_token"),
97                    static_token.as_value(),
98                );
99            }
100            BackendAction::Terminate { kind, reason } => {
101                visit.visit_entry(valuable::Value::String("kind"), kind.as_value());
102                visit.visit_entry(valuable::Value::String("reason"), reason.as_value());
103            }
104        }
105    }
106}
107
108impl valuable::Mappable for BackendAction {
109    fn size_hint(&self) -> (usize, Option<usize>) {
110        match self {
111            BackendAction::Spawn { .. } => (2, Some(2)),
112            BackendAction::Terminate { .. } => (2, Some(2)),
113        }
114    }
115}
116
117#[derive(Serialize, Deserialize, Debug, Clone, valuable::Valuable)]
118pub struct BackendStateMessage {
119    pub event_id: BackendEventId,
120    pub backend_id: BackendName,
121    pub state: BackendState,
122
123    // #[serde(skip_serializing_if = "Option::is_none")]
124    // pub address: Option<BackendAddr>,
125    pub timestamp: LoggableTime,
126}
127
128#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, valuable::Valuable)]
129pub struct BackendEventId(i64);
130
131impl From<i64> for BackendEventId {
132    fn from(i: i64) -> Self {
133        Self(i)
134    }
135}
136
137impl From<BackendEventId> for i64 {
138    fn from(id: BackendEventId) -> Self {
139        id.0
140    }
141}
142
143#[derive(Serialize, Deserialize, Debug, Clone)]
144pub struct RenewKeyRequest {
145    pub backend: BackendName,
146
147    pub local_time: LoggableTime,
148}
149
150#[derive(Serialize, Deserialize, Debug, Clone)]
151pub struct Heartbeat {
152    pub local_time: LoggableTime,
153}
154
155#[derive(Serialize, Deserialize, Debug, Clone)]
156pub enum MessageFromDrone {
157    Heartbeat(Heartbeat),
158    BackendEvent(BackendStateMessage),
159    BackendMetrics(BackendMetricsMessage),
160    AckAction { action_id: BackendActionName },
161    RenewKey(RenewKeyRequest),
162}
163
164#[derive(Serialize, Deserialize, Debug, Clone)]
165pub struct BackendMetricsMessage {
166    pub backend_id: BackendName,
167    /// Memory used by backend excluding inactive file cache, same as use shown by docker stats
168    /// ref: https://github.com/docker/cli/blob/master/cli/command/container/stats_helpers.go#L227C45-L227C45
169    pub mem_used: u64,
170    /// Memory used by backend in bytes
171    /// (calculated using kernel memory used by cgroup + page cache memory used by cgroup)
172    pub mem_total: u64,
173    /// Active memory (non reclaimable)
174    pub mem_active: u64,
175    /// Inactive memory (reclaimable)
176    pub mem_inactive: u64,
177    /// Unevictable memory (mlock etc)
178    pub mem_unevictable: u64,
179    /// The backend's memory limit
180    pub mem_limit: u64,
181    /// Nanoseconds of CPU used by backend since last message
182    pub cpu_used: u64,
183    /// Total CPU nanoseconds for system since last message
184    pub sys_cpu: u64,
185}
186
187impl ChannelMessage for MessageFromDrone {
188    type Reply = MessageToDrone;
189}
190
191#[derive(Serialize, Deserialize, Debug, Clone)]
192pub struct RenewKeyResponse {
193    /// The backend whose associated key was renewed.
194    pub backend: BackendName,
195
196    /// The key that was renewed, if successful.
197    /// If the key was not renewed, this will be None.
198    pub deadlines: Option<KeyDeadlines>,
199}
200
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct BackendActionMessage {
203    pub action_id: BackendActionName,
204    pub backend_id: BackendName,
205    pub drone_id: NodeId,
206    pub action: BackendAction,
207}
208
209#[derive(Serialize, Deserialize, Debug, Clone)]
210pub enum MessageToDrone {
211    Action(BackendActionMessage),
212    /// Acknowledge that the container has received and processed a backend event.
213    AckEvent {
214        event_id: BackendEventId,
215    },
216    RenewKeyResponse(RenewKeyResponse),
217}
218
219impl ChannelMessage for MessageToDrone {
220    type Reply = MessageFromDrone;
221}
222
223#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
224pub struct RouteInfo {
225    pub backend_id: BackendName,
226    pub address: BackendAddr,
227    pub secret_token: SecretToken,
228    pub cluster: ClusterName,
229    pub user: Option<String>,
230    pub user_data: Option<serde_json::Value>,
231    pub subdomain: Option<Subdomain>,
232}
233
234#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
235pub enum CertManagerRequest {
236    /// Request a certificate lease for a cluster.
237    CertLeaseRequest,
238
239    /// Set the TXT record for a cluster. Fails if another proxy
240    /// has more recently been granted the lease.
241    SetTxtRecord { txt_value: String },
242
243    /// Release a certificate lease for a cluster so that another
244    /// proxy can request it immediately.
245    ReleaseCertLease,
246}
247
248impl ChannelMessage for CertManagerRequest {
249    type Reply = CertManagerResponse;
250}
251
252#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, valuable::Valuable)]
253pub enum CertManagerResponse {
254    /// Acknowledge a lease request and indicate whether it was accepted.
255    CertLeaseResponse { accepted: bool },
256
257    /// Acknowledge a TXT record update and indicate whether it was accepted.
258    SetTxtRecordResponse { accepted: bool },
259}
260
261#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
262pub struct RouteInfoRequest {
263    pub token: BearerToken,
264}
265
266impl ChannelMessage for RouteInfoRequest {
267    type Reply = RouteInfoResponse;
268}
269
270#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
271pub struct RouteInfoResponse {
272    pub token: BearerToken,
273    pub route_info: Option<RouteInfo>,
274}
275
276impl ChannelMessage for RouteInfoResponse {
277    type Reply = RouteInfoRequest;
278}
279
280impl ChannelMessage for CertManagerResponse {
281    type Reply = CertManagerRequest;
282}
283
284#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
285pub enum MessageFromProxy {
286    RouteInfoRequest(RouteInfoRequest),
287    KeepAlive(BackendName),
288    CertManagerRequest(CertManagerRequest),
289}
290
291impl ChannelMessage for MessageFromProxy {
292    type Reply = MessageToProxy;
293}
294
295#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
296pub enum MessageToProxy {
297    RouteInfoResponse(RouteInfoResponse),
298    CertManagerResponse(CertManagerResponse),
299    BackendRemoved { backend: BackendName },
300}
301
302impl ChannelMessage for MessageToProxy {
303    type Reply = MessageFromProxy;
304}
305
306#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, valuable::Valuable)]
307pub enum MessageFromDns {
308    TxtRecordRequest { cluster: ClusterName },
309}
310
311impl ChannelMessage for MessageFromDns {
312    type Reply = MessageToDns;
313}
314
315#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
316pub enum MessageToDns {
317    TxtRecordResponse {
318        cluster: ClusterName,
319        txt_value: Option<String>,
320    },
321}
322
323impl ChannelMessage for MessageToDns {
324    type Reply = MessageFromDns;
325}
326
327#[derive(Serialize, Deserialize, Debug)]
328pub struct StatusResponse {
329    pub status: String,
330    pub version: String,
331    pub hash: String,
332}