Skip to main content

zlayer_types/api/
nodes.rs

1//! Node management API DTOs.
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7/// Node summary for list operations
8#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
9pub struct NodeSummary {
10    /// Node identifier
11    pub id: u64,
12    /// Node network address
13    pub address: String,
14    /// Current node status (e.g., "ready", "notready", "disconnected")
15    pub status: String,
16    /// Node role (e.g., "leader", "worker")
17    pub role: String,
18    /// Node labels for scheduling
19    pub labels: HashMap<String, String>,
20    /// Last seen timestamp (Unix timestamp)
21    pub last_seen: u64,
22}
23
24/// Node resource information
25#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
26pub struct NodeResourceInfo {
27    /// Total CPU cores
28    pub cpu_total: f64,
29    /// Used CPU cores
30    pub cpu_used: f64,
31    /// CPU usage percentage
32    pub cpu_percent: f64,
33    /// Total memory in bytes
34    pub memory_total: u64,
35    /// Used memory in bytes
36    pub memory_used: u64,
37    /// Memory usage percentage
38    pub memory_percent: f64,
39}
40
41/// Detailed node information
42#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
43pub struct NodeDetails {
44    /// Node identifier
45    pub id: u64,
46    /// Node network address
47    pub address: String,
48    /// Current node status
49    pub status: String,
50    /// Node role
51    pub role: String,
52    /// Node labels for scheduling
53    pub labels: HashMap<String, String>,
54    /// Last seen timestamp (Unix timestamp)
55    pub last_seen: u64,
56    /// Node resource information
57    pub resources: NodeResourceInfo,
58    /// Services running on this node
59    pub services: Vec<String>,
60    /// When the node was registered (Unix timestamp)
61    pub registered_at: u64,
62    /// Last heartbeat timestamp (Unix timestamp)
63    pub last_heartbeat: u64,
64}
65
66/// Request to update node labels
67#[derive(Debug, Deserialize, utoipa::ToSchema)]
68pub struct UpdateLabelsRequest {
69    /// Labels to add or update
70    pub labels: HashMap<String, String>,
71    /// Label keys to remove
72    pub remove: Vec<String>,
73}
74
75/// Response after updating labels
76#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
77pub struct UpdateLabelsResponse {
78    /// Current labels after the update
79    pub labels: HashMap<String, String>,
80}
81
82/// Join token response for cluster joining
83#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
84pub struct JoinTokenResponse {
85    /// Join token for authenticating the new node
86    pub token: String,
87    /// Leader endpoint to connect to
88    pub leader_endpoint: String,
89    /// Leader's public key for secure communication
90    pub leader_public_key: String,
91    /// Overlay network CIDR
92    pub overlay_cidr: String,
93    /// Allocated IP address for the joining node
94    pub allocated_ip: String,
95    /// Token expiration timestamp (Unix timestamp)
96    pub expires_at: u64,
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_node_summary_serialize() {
105        let summary = NodeSummary {
106            id: 1,
107            address: "192.168.1.10:9090".to_string(),
108            status: "ready".to_string(),
109            role: "worker".to_string(),
110            labels: HashMap::from([("zone".to_string(), "us-east-1".to_string())]),
111            last_seen: 1_706_745_600,
112        };
113        let json = serde_json::to_string(&summary).unwrap();
114        assert!(json.contains("192.168.1.10"));
115        assert!(json.contains("ready"));
116        assert!(json.contains("worker"));
117    }
118
119    #[test]
120    fn test_node_details_serialize() {
121        let details = NodeDetails {
122            id: 1,
123            address: "192.168.1.10:9090".to_string(),
124            status: "ready".to_string(),
125            role: "worker".to_string(),
126            labels: HashMap::new(),
127            last_seen: 1_706_745_600,
128            resources: NodeResourceInfo {
129                cpu_total: 4.0,
130                cpu_used: 1.5,
131                cpu_percent: 37.5,
132                memory_total: 8_589_934_592,
133                memory_used: 4_294_967_296,
134                memory_percent: 50.0,
135            },
136            services: vec!["api".to_string(), "worker".to_string()],
137            registered_at: 1_706_659_200,
138            last_heartbeat: 1_706_745_600,
139        };
140        let json = serde_json::to_string(&details).unwrap();
141        assert!(json.contains("resources"));
142        assert!(json.contains("services"));
143        assert!(json.contains("cpu_total"));
144    }
145
146    #[test]
147    fn test_update_labels_request_deserialize() {
148        let json = r#"{"labels": {"zone": "us-west-2"}, "remove": ["old-label"]}"#;
149        let request: UpdateLabelsRequest = serde_json::from_str(json).unwrap();
150        assert_eq!(request.labels.get("zone"), Some(&"us-west-2".to_string()));
151        assert_eq!(request.remove, vec!["old-label".to_string()]);
152    }
153
154    #[test]
155    fn test_join_token_response_serialize() {
156        let response = JoinTokenResponse {
157            token: "abc123".to_string(),
158            leader_endpoint: "192.168.1.1:9090".to_string(),
159            leader_public_key: "pubkey".to_string(),
160            overlay_cidr: "10.0.0.0/16".to_string(),
161            allocated_ip: "10.0.1.5".to_string(),
162            expires_at: 1_706_832_000,
163        };
164        let json = serde_json::to_string(&response).unwrap();
165        assert!(json.contains("abc123"));
166        assert!(json.contains("overlay_cidr"));
167    }
168}