1use core::str::FromStr;
7use std::prelude::v1::*;
8
9use std::collections::BTreeMap;
10use std::sync::Arc;
11use serde::{ Deserialize, Serialize };
12
13use product_os_capabilities::{Features, ServiceError, Services, What};
14use product_os_security::{AsByteVector, DHKeyStore, RandomGenerator, certificates::Certificates, RandomGeneratorTemplate, RNG, StdRng, SeedableRng};
15use product_os_store::ProductOSKeyValueStore;
16
17use chrono::{DateTime, Utc };
18use parking_lot::Mutex;
19use product_os_request::Uri;
20
21#[derive(Debug, Deserialize, Serialize)]
26#[serde(rename_all = "camelCase")]
27pub struct Node {
28 id: uuid::Uuid,
29 machine_id: String,
30
31 uri: String,
32 process_id: u32,
33
34 certificate: Vec<u8>,
35
36 capabilities: Vec<String>,
37 services: Services,
38 features: Features,
39
40 failures: u8,
41 created_at: DateTime<Utc>,
42 updated_at: DateTime<Utc>
43}
44
45impl AsByteVector for &Node {
46 fn as_byte_vector(&self) -> Vec<u8> {
47 let mut bytes = vec!();
48
49 bytes.extend_from_slice(self.id.as_bytes());
50 bytes.extend_from_slice(self.machine_id.as_bytes());
51 bytes.extend_from_slice(self.uri.as_bytes());
52 bytes.extend_from_slice(&self.certificate);
53 bytes.extend_from_slice(&[self.failures]);
54 bytes.extend_from_slice(self.created_at.to_string().as_bytes());
55 bytes.extend_from_slice(self.updated_at.to_string().as_bytes());
56
57 bytes
58 }
59}
60
61
62impl Node {
63 pub fn new(config: &crate::config::CommandControl, certificates: Certificates) -> Self {
73 let machine_uid = match machine_uid::get() {
74 Ok(uid) => uid,
75 Err(e) => panic!("Unable to generate machine id: {}", e)
76 };
77
78 Self {
79 id: uuid::Uuid::new_v4(),
80 uri: Uri::from_str(config.url_address.as_str()).unwrap().to_string(),
81 process_id: std::process::id(),
82 machine_id: product_os_security::create_string_hash(machine_uid.as_str()),
83 certificate: certificates.certificates.first().unwrap().to_owned(),
84 capabilities: Vec::new(),
85 services: Services::new(),
86 features: Features::new(),
87 failures: 0,
88 created_at: Utc::now(),
89 updated_at: Utc::now()
90 }
91 }
92
93 #[deprecated(since = "0.0.28", note = "Renamed to Node::new() to follow Rust conventions")]
95 pub fn default(config: &crate::config::CommandControl, certificates: Certificates) -> Self {
96 Self::new(config, certificates)
97 }
98
99 pub fn get_identifier(&self) -> String {
101 self.id.to_string()
102 }
103
104 #[deprecated(since = "0.0.28", note = "Use try_get_protocol() to avoid potential panics")]
108 pub fn get_protocol(&self) -> String {
109 let uri = Uri::from_str(self.uri.as_str()).unwrap();
110 match uri.scheme() {
111 None => String::new(),
112 Some(s) => s.to_string()
113 }
114 }
115
116 pub fn try_get_protocol(&self) -> Option<String> {
118 Uri::from_str(self.uri.as_str())
119 .ok()
120 .and_then(|uri| uri.scheme().map(|s| s.to_string()))
121 }
122
123 #[deprecated(since = "0.0.28", note = "Use try_get_address() to avoid potential panics")]
130 pub fn get_address(&self) -> Uri {
131 Uri::from_str(self.uri.as_str()).unwrap()
132 }
133
134 pub fn try_get_address(&self) -> Option<Uri> {
136 Uri::from_str(self.uri.as_str()).ok()
137 }
138
139 pub fn get_process_id(&self) -> u32 {
141 self.process_id
142 }
143
144 pub fn get_certificate(&self) -> Vec<u8> {
146 self.certificate.to_owned()
147 }
148
149 pub fn get_failures(&self) -> u8 {
151 self.failures
152 }
153
154 pub fn get_features(&self) -> &Features {
156 &self.features
157 }
158
159 pub fn get_services(&self) -> &Services {
161 &self.services
162 }
163
164 pub fn match_node(&self, selector: &str, search_value: &str) -> bool {
173 match selector {
174 "feature" => {
175 self.features.get(search_value).is_some()
176 },
177 "capability" => {
178 self.capabilities.contains(&search_value.to_string())
179 },
180 "service.kind" => {
181 self.services.find(search_value).is_some()
182 },
183 "service.enabled" => {
184 self.services.list().all(|(_, service)| service.enabled.to_string() == search_value)
185 },
186 "service.active" => {
187 self.services.list().all(|(_, service)| service.active.to_string() == search_value)
188 },
189 _ => true
190 }
191 }
192
193 pub fn match_node_query(&self, query: &BTreeMap<&str, &str>) -> bool {
198 query.iter().all(|(selector, value)| self.match_node(selector, value))
199 }
200
201 pub fn get_created_at(&self) -> DateTime<Utc> {
203 self.created_at
204 }
205
206 pub fn get_last_updated_at(&self) -> DateTime<Utc> {
208 self.updated_at
209 }
210}
211
212
213
214pub struct Registry {
220 me: Node,
221 nodes: BTreeMap<String, Node>,
222 key_store: DHKeyStore,
223
224 store: Arc<ProductOSKeyValueStore>,
225
226 max_failures: u8
227}
228
229impl Registry {
230 pub fn new(config: &crate::config::CommandControl, key_value_store: Arc<ProductOSKeyValueStore>, certificates: Certificates) -> Self {
234 let me = Node::new(config, certificates);
235
236 Registry {
237 me,
238 nodes: BTreeMap::new(),
239 key_store: DHKeyStore::new(),
240 store: key_value_store,
241 max_failures: config.max_failures,
242 }
243 }
244
245 pub fn get_max_failures(&self) -> u8 {
247 self.max_failures
248 }
249
250 async fn upsert_me_remote(&mut self) {
251 self.store.group_set(self.me.id.to_string().as_str(), serde_json::to_string(&self.me).unwrap().as_str()).unwrap_or_default()
252 }
253
254 async fn upsert_node_remote(&mut self, node: &Node) {
255 tracing::info!("Upserting node: {:?}", node);
256 self.store.group_set(node.id.to_string().as_str(), serde_json::to_string(node).unwrap().as_str()).unwrap_or_default();
257 }
258
259 async fn remove_node_remote(&mut self, identifier: &str) {
260 self.store.group_remove(identifier).unwrap_or_default()
261 }
262
263 async fn get_node_remote(&mut self, id: &str) -> Option<Node> {
264 match self.store.group_get(id) {
265 Ok(v) => {
266 let mut node: Node = serde_json::from_str(v.as_str()).unwrap();
267 let _ = node.features.setup_router();
268 Some(node)
269 },
270 Err(_) => None
271 }
272 }
273
274 pub async fn check_me_remote(&mut self) -> Option<&Node> {
279 let id = self.me.id.to_string();
280
281 match self.get_node_remote(id.as_str()).await {
282 Some(node) => {
283 if node.id != self.me.id {
284 None
285 }
286 else {
287 Some(&self.me)
288 }
289 },
290 None => { None }
291 }
292 }
293
294 pub fn get_me(&self) -> &Node {
296 &self.me
297 }
298
299 pub async fn update_me(&mut self) {
301 self.upsert_me_remote().await;
302 }
303
304 pub fn update_me_status(&mut self, success: bool) -> bool {
309 let failures = if success { 0 } else { self.me.failures + 1 };
310
311 if failures < self.max_failures {
312 self.me.failures = failures;
313 self.me.updated_at = Utc::now();
314
315 true
316 }
317 else {
318 false
319 }
320 }
321
322 pub async fn update_pulse_status(&mut self, id: &str, success: bool) -> bool {
327 match self.get_node_remote(id).await {
328 Some(mut node) => {
329 let failures = if success { 0 } else { node.failures + 1 };
330
331 if failures < self.max_failures {
332 node.failures = failures;
333 node.updated_at = Utc::now();
334
335 self.upsert_node_remote(&node).await;
336 self.nodes.insert(node.id.to_string(), node);
337
338 true
339 }
340 else {
341 tracing::info!("Removing node due to failures count {}: {:?}", failures, node);
342 self.remove_node(node.id.to_string().as_str()).await;
343
344 false
345 }
346 },
347 None => false
348 }
349 }
350
351 pub fn upsert_node_local(&mut self, identifier: String, mut node: Node) {
353 let _ = node.features.setup_router();
354 self.nodes.insert(identifier, node);
355 }
356
357 pub fn find_nodes(&self, query: BTreeMap<&str, &str>, exclude_me: bool) -> BTreeMap<String, &Node> {
359 let mut result = BTreeMap::new();
360 let me = self.me.id.to_string();
361
362 for (id, node) in &self.nodes {
363 if (!exclude_me || !me.eq(id)) && node.match_node_query(&query) {
364 result.insert(id.to_string(), node);
365 }
366 }
367
368 result
369 }
370
371 pub fn get_node(&self, id: &str) -> Option<&Node> {
373 self.nodes.get(id)
374 }
375
376 pub fn get_nodes(&self, skip: u8, exclude_me: bool) -> BTreeMap<String, &Node> {
378 let me = self.me.id.to_string();
379
380 self.nodes.iter()
381 .filter(|(id, _)| !exclude_me || !me.eq(*id))
382 .skip(skip as usize)
383 .map(|(id, node)| (id.to_string(), node))
384 .collect()
385 }
386
387 #[deprecated(since = "0.0.28", note = "Use get_nodes_raw_certificates instead, which has identical behavior")]
389 pub fn get_nodes_certificates(&self, skip: u8, exclude_me: bool) -> Vec<Vec<u8>> {
390 self.get_nodes_raw_certificates(skip, exclude_me)
391 }
392
393 pub fn get_nodes_raw_certificates(&self, skip: u8, exclude_me: bool) -> Vec<Vec<u8>> {
395 let me = self.me.id.to_string();
396
397 self.nodes.iter()
398 .filter(|(id, _)| !exclude_me || !me.eq(*id))
399 .skip(skip as usize)
400 .map(|(_, node)| node.get_certificate())
401 .collect()
402 }
403
404 pub fn get_nodes_endpoints(&self, skip: u8, exclude_me: bool) -> BTreeMap<String, (Uri, Option<Vec<u8>>)> {
406 let me = self.me.id.to_string();
407
408 #[allow(deprecated)]
409 self.nodes.iter()
410 .filter(|(id, _)| !exclude_me || !me.eq(*id))
411 .skip(skip as usize)
412 .map(|(id, node)| {
413 let address = node.get_address();
414 let key = self.get_key(id);
415 (id.to_string(), (address, key))
416 })
417 .collect()
418 }
419
420 pub async fn remove_node(&mut self, identifier: &str) -> Option<Node> {
422 match self.nodes.remove(identifier) {
423 Some(node) => {
424 self.remove_node_remote(node.id.to_string().as_str()).await;
425 Some(node)
426 },
427 None => None
428 }
429 }
430
431 pub fn pick_node(&self, query: BTreeMap<&str, &str>) -> Option<&Node> {
433 let eligible_nodes: Vec<&Node> = self.find_nodes(query, false).values().copied().collect();
434 let select = RandomGenerator::new(Some(RNG::Std(StdRng::from_entropy()))).get_random_usize(0, eligible_nodes.len());
435
436 eligible_nodes.get(select).copied()
437 }
438
439 pub fn pick_node_for_capability(&self, capability: &str) -> Option<&Node> {
441 let mut query = BTreeMap::new();
442 query.insert("capability", capability);
443 self.pick_node(query)
444 }
445
446 pub async fn add_feature(&mut self, feature: Arc<dyn product_os_capabilities::Feature>, base_path: String, router: &mut product_os_router::ProductOSRouter) {
448 let _ = self.me.features.add(feature, base_path, router).await;
449 self.update_me().await;
450 }
451
452 pub async fn add_feature_mut(&mut self, feature: Arc<Mutex<dyn product_os_capabilities::Feature>>, base_path: String, router: &mut product_os_router::ProductOSRouter) {
454 let _ = self.me.features.add_mut(feature, base_path, router).await;
455 self.update_me().await;
456 }
457
458 pub fn pick_node_for_feature(&self, feature: &str) -> Option<&Node> {
460 let mut query = BTreeMap::new();
461 query.insert("feature", feature);
462 self.pick_node(query)
463 }
464
465 pub async fn remove_feature(&mut self, identifier: &str) {
467 let _ = self.me.features.remove(identifier);
468 self.update_me().await;
469 }
470
471 pub async fn add_service(&mut self, service: Arc<dyn product_os_capabilities::Service>) {
473 self.me.services.add(service).await;
474 self.update_me().await;
475 }
476
477 pub async fn add_service_mut(&mut self, service: Arc<Mutex<dyn product_os_capabilities::Service>>) {
479 let _ = self.me.services.add_mut(service).await;
480 self.update_me().await;
481 }
482
483 pub async fn set_service_active(&mut self, identifier: String, status: bool) {
485 let id = identifier.as_str();
486 match self.me.services.get_mut(id) {
487 None => (),
488 Some(s) => {
489 s.active = status;
490 self.update_me().await;
491 }
492 }
493 }
494
495 pub async fn remove_service(&mut self, identifier: &str) {
497 self.me.services.remove(identifier);
498 self.update_me().await;
499 }
500
501 pub async fn remove_inactive_services(&mut self, query: BTreeMap<&str, &str>) {
503 let mut matches = Vec::new();
504
505 for (identifier, _) in self.find_nodes(query, true) {
506 matches.push(identifier.to_owned());
507 }
508
509 for identifier in &matches {
510 self.remove_service(identifier).await;
511 }
512
513 if !matches.is_empty() { self.update_me().await };
514 }
515
516 pub async fn start_services(&mut self) -> Result<(), ()> {
518 for (_, service) in self.me.services.list_mut() {
519 match service.start().await {
520 Ok(_) => {}
521 Err(_) => return Err(())
522 }
523 }
524
525 Ok(())
526 }
527
528 pub async fn start_service(&mut self, identifier: &str) -> Result<(), ()> {
530 match self.me.services.get_mut(identifier) {
531 None => Err(()),
532 Some(s) => s.start().await
533 }
534 }
535
536 pub async fn stop_service(&mut self, identifier: &str) -> Result<(), ()> {
538 match self.me.services.get_mut(identifier) {
539 None => Err(()),
540 Some(s) => s.stop().await
541 }
542 }
543
544 pub async fn restart_service(&mut self, identifier: &str) -> Result<(), ()> {
546 match self.me.services.get_mut(identifier) {
547 None => Err(()),
548 Some(s) => s.restart().await
549 }
550 }
551
552 pub async fn call_service(&mut self, identifier: &str, action: &What, input: &Option<serde_json::Value>) -> Result<Option<serde_json::Value>, ServiceError> {
554 match self.me.services.get_mut(identifier) {
555 None => Err(ServiceError::GenericError(format!("Service {} not found", identifier))),
556 Some(s) => s.call(action, input).await
557 }
558 }
559
560 pub async fn discover_nodes(&mut self) {
562 let mut nodes = BTreeMap::new();
563
564 match self.store.group_find(None) {
565 Ok(ns) => {
566 nodes = ns;
567 },
568 Err(_) => {
569 tracing::error!("Error getting nodes from store");
570 }
571 }
572
573 for (id, node) in nodes {
574 match serde_json::from_str(node.as_str()) {
575 Ok(n) => {
576 let mut node: Node = n;
577 let _ = node.features.setup_router();
578 tracing::trace!("Importing remote node: {:?}", node.id);
579 self.upsert_node_local(node.id.to_string(), node);
580 },
581 Err(e) => {
582 tracing::error!("Error importing remote node {} - purging: {:?}", id, e);
583 self.remove_node_remote(id.as_str()).await;
584 }
585 }
586 }
587 }
588
589 pub fn get_key(&self, identifier: &str) -> Option<Vec<u8>> {
591 self.key_store.get_key(identifier).map(|k| k.to_vec())
592 }
593
594 pub fn create_key_session(&mut self) -> (String, [u8; 32]) {
596 self.key_store.create_session()
597 }
598
599 pub fn generate_key(&mut self, session_identifier: &str, remote_public_key: &[u8], association: String, remote_session_identifier: Option<String>) {
601 self.key_store.generate_key(session_identifier, remote_public_key, association, remote_session_identifier);
602 }
603}