1#![no_std]
79#![warn(rust_2018_idioms)]
80#![warn(clippy::all)]
81#![allow(clippy::module_name_repetitions)]
82#![allow(clippy::must_use_candidate)]
83#![allow(clippy::too_many_arguments)]
84#![allow(clippy::missing_errors_doc)]
85#![allow(clippy::missing_panics_doc)]
86#![allow(missing_docs)]
87#![allow(ambiguous_glob_imports)]
88#![allow(ambiguous_panic_imports)]
89
90extern crate no_std_compat as std;
91
92use std::prelude::v1::*;
93
94mod config;
95pub use config::CommandControl;
96
97pub mod authentication;
98mod registry;
99pub mod commands;
100
101use std::collections::BTreeMap;
102
103use std::sync::Arc;
104#[cfg(feature = "distributed")]
105use product_os_request::Bytes;
106use parking_lot::Mutex;
107#[cfg(feature = "distributed")]
108use parking_lot::MutexGuard;
109#[cfg(feature = "relational_store")]
110use product_os_store::{KeyValueKind, KeyValueStore, RelationalKind, RelationalStore};
111#[cfg(not(feature = "relational_store"))]
112use product_os_store::{KeyValueKind, KeyValueStore};
113
114use std::str::FromStr;
115use std::time::Duration;
116
117pub use product_os_request::Method;
118use product_os_request::{ProductOSClient, ProductOSResponse};
119pub use registry::Node;
120
121const ENABLED_STR: &str = "true";
123
124
125pub struct ProductOSController {
153 command_control: crate::config::CommandControl,
154
155 certificates: product_os_security::certificates::Certificates,
156
157 max_servers: u8,
158
159 registry: registry::Registry,
160
161 pulse_check: bool,
162 pulse_check_cron: String,
163
164 #[cfg(feature = "monitor")]
165 monitor: bool,
166 #[cfg(feature = "monitor")]
167 monitor_cron: String,
168
169 requester: product_os_request::ProductOSRequester,
170 client: product_os_request::ProductOSRequestClient,
171
172 #[cfg(feature = "relational_store")]
173 relational_store: Arc<product_os_store::ProductOSRelationalStore>,
174 key_value_store: Arc<product_os_store::ProductOSKeyValueStore>,
175
176 pub on_presence_lost: Option<Arc<dyn Fn() + Send + Sync>>,
179}
180
181impl ProductOSController {
182 pub fn new(command_control: crate::config::CommandControl, certificates:
194 product_os_security::certificates::Certificates,
195 key_value_store: Option<Arc<product_os_store::ProductOSKeyValueStore>>,
196 #[cfg(feature = "relational_store")] relational_store: Option<Arc<product_os_store::ProductOSRelationalStore>>) -> Self {
197 let key_value_store: Arc<product_os_store::ProductOSKeyValueStore> = key_value_store.unwrap_or_else(|| Arc::new(product_os_store::ProductOSKeyValueStore::try_new(&KeyValueStore {
198 enabled: false,
199 kind: KeyValueKind::Sink,
200 host: "".to_string(),
201 port: 0,
202 secure: false,
203 db_number: 0,
204 db_name: None,
205 username: None,
206 password: None,
207 pool_size: 0,
208 default_limit: 0,
209 default_offset: 0,
210 prefix: None,
211 }).expect("Sink key-value store creation should not fail")));
212
213 #[cfg(feature = "relational_store")]
214 let relational_store: Arc<product_os_store::ProductOSRelationalStore> = relational_store.unwrap_or_else(|| Arc::new(product_os_store::ProductOSRelationalStore::try_new(&RelationalStore {
215 enabled: false,
216 kind: RelationalKind::Sink,
217 host: "".to_string(),
218 port: 0,
219 secure: false,
220 db_name: "".to_string(),
221 username: None,
222 password: None,
223 pool_size: 0,
224 default_limit: 0,
225 default_offset: 0,
226 prefix: None,
227 }).expect("Sink relational store creation should not fail")));
228
229 let registry = registry::Registry::new(&command_control, key_value_store.clone(), certificates.to_owned());
230
231 let mut requester = product_os_request::ProductOSRequester::new();
232 requester.add_header("x-product-os-command", registry.get_me().get_identifier().as_str(), false);
233 let mut request_client = product_os_request::ProductOSRequestClient::new();
234 request_client.build(&requester);
235
236 Self {
237 certificates,
238
239 max_servers: command_control.max_servers,
240
241 pulse_check: command_control.pulse_check,
242 pulse_check_cron: command_control.pulse_check_cron.clone(),
243
244 #[cfg(feature = "monitor")]
245 monitor: command_control.monitor,
246 #[cfg(feature = "monitor")]
247 monitor_cron: command_control.monitor_cron.clone(),
248
249 command_control,
250
251 #[cfg(feature = "relational_store")]
252 relational_store,
253 key_value_store,
254
255 registry,
256
257 requester,
258 client: request_client,
259
260 on_presence_lost: None,
261 }
262 }
263
264 pub fn get_registry(&self) -> ®istry::Registry {
269 &self.registry
270 }
271
272 pub async fn discover_nodes(&mut self) {
274 self.registry.discover_nodes().await;
275
276 for cert in self.registry.get_nodes_raw_certificates(0, true) {
277 self.requester.add_trusted_certificate_der(cert);
278 }
279
280 self.client.build(&self.requester);
281 }
282
283 pub fn get_max_servers(&self) -> u8 {
285 self.max_servers
286 }
287
288 pub fn upsert_node_local(&mut self, identifier: String, node: Node) {
290 self.registry.upsert_node_local(identifier, node);
291 }
292
293 pub fn get_certificates(&self) -> Vec<&[u8]> {
295 self.certificates.certificates.iter().map(|cert| cert.as_slice()).collect()
296 }
297
298 pub fn get_private_key(&self) -> &[u8] {
300 self.certificates.private.as_slice()
301 }
302
303 pub fn get_certificates_and_private_key(&self) -> product_os_security::certificates::Certificates {
305 self.certificates.to_owned()
306 }
307
308 pub fn validate_certificate(&self, certificate: &[u8]) -> bool {
310 tracing::trace!("Checking certificate stored {:?} vs given {:?}", self.certificates.certificates, certificate);
311
312 for cert in self.get_certificates() {
313 if cert.eq(certificate) { return true; }
314 }
315
316 false
317 }
318
319 pub fn validate_verify_tag<T>(&self, query: Option<&BTreeMap<String, String>>, payload: Option<T>,
321 headers: Option<&BTreeMap<String, String>>, verify_identifier: &str, verify_tag: &str) -> bool
322 where T: product_os_security::AsByteVector
323 {
324 tracing::trace!("Checking verification provided {} from {}", verify_tag, verify_identifier);
325 let key = self.registry.get_key(verify_identifier);
326 tracing::trace!("Checking verification with key {:?}", key);
327
328 match key {
329 Some(verify_key) => {
330 product_os_security::verify_auth_request(query, false, payload, headers,
331 &["x-product-os-verify", "x-product-os-command", "x-product-os-control"],
332 verify_tag, Some(verify_key.as_slice()))
333 },
334 None => false
335 }
336 }
337
338 pub fn authenticate_command_control<T>(&self, query: Option<&BTreeMap<String, String>>, payload: Option<T>, headers: Option<&BTreeMap<String, String>>,
342 verify_identifier: Option<&str>, verify_tag: Option<&str>) -> Result<bool, authentication::CommandControlAuthenticateError>
343 where T: product_os_security::AsByteVector
344 {
345 tracing::trace!("Verify identifier {:?} and tag {:?} verify result", &verify_identifier, &verify_tag);
346 if verify_identifier.is_some() && verify_tag.is_some() && self.validate_verify_tag(query, payload, headers, verify_identifier.unwrap(), verify_tag.unwrap()) {
347 Ok(true)
348 }
349 else {
350 Err(authentication::CommandControlAuthenticateError {
351 error: authentication::CommandControlAuthenticateErrorState::KeyError("One of the keys provided was not valid".to_string())
352 })
353 }
354 }
355
356 pub fn search_and_prepare_command(&self, query: BTreeMap<&str, &str>, module: String, instruction: String, data: Option<serde_json::Value>) -> Result<crate::commands::Command, product_os_request::ProductOSRequestError> {
358 let matching_node = self.registry.pick_node(query);
359 let client = &self.client;
360
361 match matching_node {
362 Some(node) => {
363 match self.registry.get_key(node.get_identifier().as_str()) {
364 None => Err(product_os_request::ProductOSRequestError::Error("No key found for matching node".to_string())),
365 Some(key) => Ok(commands::Command {
366 requester: client.clone(),
367 #[allow(deprecated)]
368 node_url: node.get_address().to_owned(),
369 verify_key: key,
370 module,
371 instruction,
372 data
373 })
374 }
375 },
376 None => Err(product_os_request::ProductOSRequestError::Error("No matching node found".to_string()))
377 }
378 }
379
380 pub fn find_and_prepare_command(&self, key: &str, manager: &str, instruction: String, data: Option<serde_json::Value>) -> Result<commands::Command, product_os_request::ProductOSRequestError> {
382 let query = BTreeMap::from([
383 ("manager.key", key),
384 ("manager.kind", manager),
385 ("manager.enabled", ENABLED_STR)
386 ]);
387
388 self.search_and_prepare_command(query, manager.to_string(), instruction, data)
389 }
390
391 pub fn find_any_and_prepare_command(&self, capability: &str, manager: &str, instruction: &str, data: Option<serde_json::Value>) -> Result<commands::Command, product_os_request::ProductOSRequestError> {
393 let query = BTreeMap::from([
394 ("capability", capability),
395 ("manager.enabled", ENABLED_STR)
396 ]);
397
398 self.search_and_prepare_command(query, manager.to_string(), instruction.to_string(), data)
399 }
400
401 #[deprecated(since = "0.0.28", note = "Use find_kind_and_prepare_command_sync instead; this method is not actually async")]
406 pub async fn find_kind_and_prepare_command(&self, kind: &str, manager: &str, instruction: &str, data: Option<serde_json::Value>) -> Result<commands::Command, product_os_request::ProductOSRequestError> {
407 self.find_kind_and_prepare_command_sync(kind, manager, instruction, data)
408 }
409
410 pub fn find_kind_and_prepare_command_sync(&self, kind: &str, manager: &str, instruction: &str, data: Option<serde_json::Value>) -> Result<commands::Command, product_os_request::ProductOSRequestError> {
412 let query = BTreeMap::from([
413 ("kind", kind),
414 ("manager.enabled", ENABLED_STR)
415 ]);
416
417 self.search_and_prepare_command(query, manager.to_string(), instruction.to_string(), data)
418 }
419
420 pub fn search_and_prepare_ask(&self, query: BTreeMap<&str, &str>, path: &str, data: Option<serde_json::Value>, headers: BTreeMap<String, String>,
422 params: BTreeMap<String, String>, method: Method) -> Result<commands::Ask, product_os_request::ProductOSRequestError> {
423 let matching_node = self.registry.pick_node(query);
424 let client = &self.client;
425 match matching_node {
426 Some(node) => {
427 match self.registry.get_key(node.get_identifier().as_str()) {
428 None => Err(product_os_request::ProductOSRequestError::Error("No key found for matching node".to_string())),
429 Some(key) => Ok(commands::Ask {
430 requester: client.clone(),
431 #[allow(deprecated)]
432 node_url: node.get_address().to_owned(),
433 verify_key: key,
434 path: path.to_string(),
435 data,
436 headers,
437 params,
438 method
439 })
440 }
441
442 },
443 None => Err(product_os_request::ProductOSRequestError::Error("No matching node found".to_string()))
444 }
445 }
446
447 pub async fn find_feature_and_ask(&self, feature: &str, path: &str, data: &Option<serde_json::Value>, headers: &BTreeMap<String, String>,
449 params: &BTreeMap<String, String>, method: &Method) -> Result<ProductOSResponse<product_os_request::BodyBytes>, product_os_request::ProductOSRequestError> {
450 let matching_node = self.registry.pick_node_for_feature(feature);
451 let client = &self.client;
452 match matching_node {
453 Some(node) => {
454 match self.registry.get_key(node.get_identifier().as_str()) {
455 None => Err(product_os_request::ProductOSRequestError::Error("No key found for matching node".to_string())),
456 Some(key) => commands::ask_node(client, node, key.as_slice(), path, data, headers, params, method).await
457 }
458
459 },
460 None => Err(product_os_request::ProductOSRequestError::Error("No matching node found".to_string()))
461 }
462 }
463
464 pub fn find_feature_and_prepare_ask(&self, feature: &str, path: &str, data: Option<serde_json::Value>, headers: BTreeMap<String, String>,
466 params: BTreeMap<String, String>, method: Method) -> Result<commands::Ask, product_os_request::ProductOSRequestError> {
467 let matching_node = self.registry.pick_node_for_feature(feature);
468 let client = &self.client;
469 match matching_node {
470 Some(node) => {
471 match self.registry.get_key(node.get_identifier().as_str()) {
472 None => Err(product_os_request::ProductOSRequestError::Error("No key found for matching node".to_string())),
473 Some(key) => Ok(commands::Ask {
474 requester: client.clone(),
475 #[allow(deprecated)]
476 node_url: node.get_address().to_owned(),
477 verify_key: key,
478 path: path.to_string(),
479 data,
480 headers,
481 params,
482 method
483 })
484 }
485
486 },
487 None => Err(product_os_request::ProductOSRequestError::Error("No matching node found".to_string()))
488 }
489 }
490
491 pub fn get_configuration(&self) -> crate::config::CommandControl {
493 self.command_control.clone()
494 }
495
496 pub fn get_key(&self, identifier: &str) -> Option<Vec<u8>> {
498 self.registry.get_key(identifier)
499 }
500
501 pub fn create_key_session(&mut self) -> (String, [u8; 32]) {
503 self.registry.create_key_session()
504 }
505
506 pub fn generate_key(&mut self, session_identifier: &str, remote_public_key: &[u8], association: String, remote_session_identifier: Option<String>) {
508 self.registry.generate_key(session_identifier, remote_public_key, association, remote_session_identifier);
509 }
510
511 #[cfg(feature = "relational_store")]
513 pub fn get_relational_store(&mut self) -> Arc<product_os_store::ProductOSRelationalStore> {
514 self.relational_store.clone()
515 }
516
517 pub fn get_key_value_store(&mut self) -> Arc<product_os_store::ProductOSKeyValueStore> {
519 self.key_value_store.clone()
520 }
521
522 pub async fn add_feature(&mut self, feature: Arc<dyn product_os_capabilities::Feature>, base_path: String, router: &mut product_os_router::ProductOSRouter) {
524 self.registry.add_feature(feature, base_path, router).await;
525 }
526
527 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) {
529 self.registry.add_feature_mut(feature, base_path, router).await;
530 }
531
532 pub async fn remove_feature(&mut self, identifier: &str) {
534 self.registry.remove_feature(identifier).await;
535 }
536
537 pub async fn add_service(&mut self, service: Arc<dyn product_os_capabilities::Service>) {
539 self.registry.add_service(service).await;
540 }
541
542 pub async fn add_service_mut(&mut self, service: Arc<Mutex<dyn product_os_capabilities::Service>>) {
544 self.registry.add_service_mut(service).await;
545 }
546
547 pub async fn set_service_active(&mut self, identifier: String, status: bool) {
549 self.registry.set_service_active(identifier, status).await;
550 }
551
552 pub async fn remove_service(&mut self, identifier: &str) {
554 self.registry.remove_service(identifier).await;
555 }
556
557 pub async fn remove_inactive_services(&mut self, query: BTreeMap<&str, &str>) {
559 self.registry.remove_inactive_services(query).await;
560 }
561
562 pub async fn start_services(&mut self) -> Result<(), ()> {
564 self.registry.start_services().await
565 }
566
567 pub async fn start_service(&mut self, identifier: &str) -> Result<(), ()> {
569 self.registry.start_service(identifier).await
570 }
571
572 pub async fn stop_service(&mut self, identifier: &str) -> Result<(), ()> {
574 self.registry.stop_service(identifier).await
575 }
576}
577
578
579
580
581#[allow(clippy::await_holding_lock)]
590pub async fn pulse_run(controller_unlocked: Arc<Mutex<ProductOSController>>) {
591 tracing::info!("Starting pulse run...");
592 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
593 match controller_locked {
594 #[allow(unused_mut)]
595 Some(mut controller) => {
596 #[cfg(feature = "distributed")]
597 controller.discover_nodes().await;
598
599 #[cfg(feature = "distributed")]
600 let self_identifier = controller.registry.get_me().get_identifier();
601 #[cfg(feature = "distributed")]
602 #[allow(deprecated)]
603 let control_url = controller.registry.get_me().get_address();
604
605 #[cfg(feature = "distributed")]
606 let nodes = controller.registry.get_nodes_endpoints(0, true);
607
608 #[cfg(feature = "distributed")] {
609 match controller.registry.check_me_remote().await {
610 Some(_) => { controller.registry.update_me_status(true); },
611 None => {
612 let alive = controller.registry.update_me_status(false);
613
614 if !alive {
615 tracing::error!("Terminating server due to lost presence on remote registry");
616 match &controller.on_presence_lost {
617 Some(callback) => callback(),
618 None => std::process::exit(8),
619 }
620 };
621 }
622 }
623 }
624
625 let services = controller.get_registry().get_me().get_services().list();
627 for (_, service) in services {
628 let _ = service.status().await;
629 }
630
631 #[cfg(feature = "distributed")]
632 let client = controller.client.clone();
633
634 std::mem::drop(controller);
636
637 #[cfg(feature = "distributed")]
638 for (identifier, (url, key)) in nodes {
639 if url != control_url {
640 match key {
641 Some(verify_key) => {
642 match commands::command(&client, url.clone(), verify_key, "status", "ping", None).await {
643 Ok(response) => {
644 let Some(status) = response.try_status() else {
645 tracing::error!("Failed to get status from response for {}", url);
646 continue;
647 };
648
649 match status {
650 product_os_request::StatusCode::UNAUTHORIZED => {
651 let text = {
652 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
653 match controller_locked {
654 Some(controller) => {
655 controller.client.text(response).await.unwrap_or_else(|e| {
656 tracing::error!("Failed to parse response text: {:?}", e);
657 String::new()
658 })
659 }
660 None => String::new()
661 }
662 };
663
664 let auth: authentication::CommandControlAuthenticateError = match serde_json::from_str(text.as_str()) {
665 Ok(auth_error) => auth_error,
666 Err(_) => authentication::CommandControlAuthenticateError { error: authentication::CommandControlAuthenticateErrorState::None }
667 };
668
669 tracing::error!("Error object auth {:?}", auth);
670
671 match auth.error {
672 authentication::CommandControlAuthenticateErrorState::KeyError(_) => {
673 tracing::info!("Attempting to purge remote node - keys don't match {}", identifier);
674
675 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
676 match controller_locked {
677 Some(mut controller) => {
678 controller.registry.remove_node(identifier.as_str()).await;
679 }
680 None => tracing::error!("Failed to lock controller")
681 }
682 },
683 authentication::CommandControlAuthenticateErrorState::None => ()
684 };
685 },
686 product_os_request::StatusCode::OK => {
687 let body = {
688 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
689 match controller_locked {
690 Some(controller) => {
691 controller.client.bytes(response).await.unwrap_or_else(|e| {
692 tracing::error!("Failed to parse response bytes: {:?}", e);
693 Bytes::new()
694 })
695 }
696 None => Bytes::new()
697 }
698 };
699
700 tracing::info!("Response received from {}: {} {:?}", url, status, body);
701 }
702 _ => {
703 let body = {
704 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
705 match controller_locked {
706 Some(controller) => {
707 controller.client.bytes(response).await.unwrap_or_else(|e| {
708 tracing::error!("Failed to parse error response bytes: {:?}", e);
709 Bytes::new()
710 })
711 }
712 None => Bytes::new()
713 }
714 };
715 tracing::error!("Error response received from {}: {} {:?}", url, status, body);
716 tracing::info!("Attempting to purge remote node - keys don't match {}", identifier);
717
718 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
719 match controller_locked {
720 Some(mut controller) => {
721 controller.registry.update_pulse_status(identifier.as_str(), false).await;
722 }
723 None => tracing::error!("Failed to lock controller")
724 }
725 }
726 }
727 },
728 Err(e) => {
729 tracing::error!("Error encountered {:?} from {}", e, url);
730
731 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
732 match controller_locked {
733 Some(mut controller) => {
734 controller.registry.update_pulse_status(identifier.as_str(), false).await;
735 }
736 None => tracing::error!("Failed to lock controller")
737 }
738 }
739 }
740 },
741 None => ()
742 }
743 } else if self_identifier != identifier {
744 tracing::info!("Attempting to purge self duplicate node - identifier mismatch {}", identifier);
745 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
746 match controller_locked {
747 Some(mut controller) => {
748 controller.registry.remove_node(identifier.as_str()).await;
749 }
750 None => tracing::error!("Failed to lock controller")
751 }
752 }
753 }
754
755
756 tracing::info!("Finished pulse run...");
757 }
758 None => tracing::error!("Failed to lock controller")
759 }
760}
761
762
763#[allow(clippy::await_holding_lock)]
767pub async fn run_controller<X, E>(controller_mutex: Arc<Mutex<ProductOSController>>, executor: Arc<E>)
768where
769 E: product_os_async_executor::Executor<X> + product_os_async_executor::ExecutorPerform<X> + product_os_async_executor::Timer
770{
771 let controller_unlocked = controller_mutex.clone();
772 let controller_locked = controller_unlocked.try_lock_for(core::time::Duration::new(10, 0));
773 match controller_locked {
774 Some(mut controller) => {
775
776 #[cfg(feature = "distributed")] {
777 authentication::perform_self_trust(&mut controller);
778
779 controller.registry.update_me().await;
780 tracing::info!("Added self...");
781
782 tracing::info!("Command and Control registered for {}", controller.registry.get_me().get_identifier());
783
784 controller.discover_nodes().await;
785 perform_announce(&mut controller).await;
786 }
787
788 if controller.command_control.auto_start_services {
790 match controller.start_services().await {
791 Ok(_) => {
792 tracing::info!("Command Control services started");
793 },
794 Err(_) => {
795 tracing::error!("Command Control services failed to start");
796 }
797 }
798 }
799
800 let pulse_check = controller.pulse_check;
801 let pulse_check_cron = cron::Schedule::from_str(controller.pulse_check_cron.as_str()).unwrap();
802 let pulse_check_next = pulse_check_cron.upcoming(chrono::Utc).next().unwrap();
803 let pulse_check_following = pulse_check_cron.upcoming(chrono::Utc).nth(1).unwrap();
804
805 #[cfg(feature = "monitor")]
806 let monitor = controller.monitor;
807 #[cfg(feature = "monitor")]
808 let monitor_cron = cron::Schedule::from_str(controller.monitor_cron.as_str()).unwrap();
809 #[cfg(feature = "monitor")]
810 let _monitor_next = monitor_cron.upcoming(chrono::Utc).next().unwrap();
811 #[cfg(feature = "monitor")]
812 let _monitor_following = monitor_cron.upcoming(chrono::Utc).nth(1).unwrap();
813
814 std::mem::drop(controller);
816
817 if pulse_check {
818 drop(E::spawn_from_executor(executor.as_ref(), async move {
819 tracing::info!("Pulse check service starting...");
820
821 let start_duration = match u32::try_from(match pulse_check_next.signed_duration_since(chrono::Utc::now()).to_std() {
822 Ok(d) => d,
823 Err(_) => Duration::new(1, 0)
824 }.as_millis()) {
825 Ok(u) => u,
826 Err(_) => panic!("Period defined is too large for timer")
827 };
828 let interval_duration = match u32::try_from(match pulse_check_following.signed_duration_since(pulse_check_next).to_std() {
829 Ok(d) => d,
830 Err(_) => Duration::new(1, 0)
831 }.as_millis()) {
832 Ok(u) => u,
833 Err(_) => panic!("Period defined is too large for timer")
834 };
835
836 let mut delay = E::once(start_duration).await;
837 let mut interval = E::interval(interval_duration).await;
838
839 delay.tick().await;
840 loop {
841 interval.tick().await;
842 tracing::debug!("Pulse check service running");
843
844 pulse_run(controller_mutex.clone()).await;
845 }
846 }));
847 }
848
849 #[cfg(feature = "monitor")]
850 if monitor {
851 drop(E::spawn_from_executor(executor.as_ref(), async move {
852 tracing::info!("Monitor service starting...");
853
854 let start_duration = match u32::try_from(match pulse_check_next.signed_duration_since(chrono::Utc::now()).to_std() {
855 Ok(d) => d,
856 Err(_) => Duration::new(1, 0)
857 }.as_millis()) {
858 Ok(u) => u,
859 Err(_) => panic!("Period defined is too large for timer")
860 };
861 let interval_duration = match u32::try_from(match pulse_check_following.signed_duration_since(pulse_check_next).to_std() {
862 Ok(d) => d,
863 Err(_) => Duration::new(1, 0)
864 }.as_millis()) {
865 Ok(u) => u,
866 Err(_) => panic!("Period defined is too large for timer")
867 };
868
869 let mut delay = E::once(start_duration).await;
870 let mut interval = E::interval(interval_duration).await;
871
872 delay.tick().await;
873 loop {
874 interval.tick().await;
875 tracing::debug!("Monitor service running");
876
877 let _ = product_os_monitoring::process_statistics_info(None);
878 }
879 }));
880 }
881 }
882 None => tracing::error!("Failed to lock controller")
883 }
884}
885
886
887#[cfg(feature = "distributed")]
892pub async fn perform_announce(controller: &mut MutexGuard<'_, ProductOSController>) {
893 tracing::info!("Announce searching for nodes...");
894
895 authentication::perform_key_exchange(controller).await;
896
897 let self_identifier = controller.registry.get_me().get_identifier();
898 #[allow(deprecated)]
899 let control_url = controller.registry.get_me().get_address();
900
901 let nodes = controller.registry.get_nodes_endpoints(0, true);
902
903 tracing::info!("Starting announce...");
904 tracing::info!("Nodes data {:?}", nodes);
905
906 for (identifier, (url, key)) in nodes {
907 if url != control_url {
908 match key {
909 Some(verify_key) => {
910 tracing::info!("Announcing {}: {}", identifier, url);
911
912 match commands::command(&controller.client, url.clone(), verify_key, "status", "announce", Some(serde_json::value::to_value(controller.get_registry().get_me()).unwrap())).await {
913 Ok(response) => {
914 let Some(status) = response.try_status() else {
915 tracing::error!("Failed to get status from announce response for {}", url);
916 continue;
917 };
918
919 match status {
920 product_os_request::StatusCode::UNAUTHORIZED => {
921 let text = controller.client.text(response).await.unwrap();
922 let auth: authentication::CommandControlAuthenticateError = match serde_json::from_str(text.as_str()) {
923 Ok(auth_error) => auth_error,
924 Err(_) => authentication::CommandControlAuthenticateError { error: authentication::CommandControlAuthenticateErrorState::None }
925 };
926
927 tracing::error!("Error object auth {:?}", auth);
928
929 match auth.error {
930 authentication::CommandControlAuthenticateErrorState::KeyError(_) => {
931 tracing::info!("Error from remote node - keys don't match {}", identifier);
932 },
933 authentication::CommandControlAuthenticateErrorState::None => ()
934 };
935 },
936 product_os_request::StatusCode::OK => {
937 let body = controller.client.bytes(response).await.unwrap();
938 tracing::info!("Response received from {}: {} {:?}", url, status, body);
939 }
940 _ => {
941 let body = controller.client.bytes(response).await.unwrap();
942 tracing::error!("Error response received from {}: {} {:?}", url, status, body);
943 }
944 }
945 },
946 Err(e) => {
947 tracing::error!("Error encountered {:?} from {}", e, url);
948 }
949 };
950 },
951 None => ()
952 }
953 }
954 else {
955 if identifier != self_identifier {
956 tracing::info!("Attempting to purge self duplicate node - identifier mismatch {}", identifier);
957 controller.registry.remove_node(identifier.as_str()).await;
958 }
959 }
960 }
961
962 tracing::info!("Finished announcing...");
963}