You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

attach.rs 7.1 kB

5 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. use communication_layer_request_reply::{TcpConnection, TcpRequestReplyConnection};
  2. use dora_core::descriptor::{CoreNodeKind, Descriptor, DescriptorExt, resolve_path};
  3. use dora_message::cli_to_coordinator::ControlRequest;
  4. use dora_message::common::LogMessage;
  5. use dora_message::coordinator_to_cli::ControlRequestReply;
  6. use eyre::Context;
  7. use notify::event::ModifyKind;
  8. use notify::{Config, Event as NotifyEvent, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
  9. use std::{
  10. collections::HashMap,
  11. net::{SocketAddr, TcpStream},
  12. };
  13. use std::{path::PathBuf, sync::mpsc, time::Duration};
  14. use tracing::{error, info};
  15. use uuid::Uuid;
  16. use crate::common::handle_dataflow_result;
  17. use crate::output::print_log_message;
  18. pub fn attach_dataflow(
  19. dataflow: Descriptor,
  20. dataflow_path: PathBuf,
  21. dataflow_id: Uuid,
  22. session: &mut TcpRequestReplyConnection,
  23. hot_reload: bool,
  24. coordinator_socket: SocketAddr,
  25. log_level: log::LevelFilter,
  26. ) -> Result<(), eyre::ErrReport> {
  27. let (tx, rx) = mpsc::sync_channel(2);
  28. // Generate path hashmap
  29. let mut node_path_lookup = HashMap::new();
  30. let nodes = dataflow.resolve_aliases_and_set_defaults()?;
  31. let print_daemon_name = nodes.values().any(|n| n.deploy.is_some());
  32. let working_dir = dataflow_path
  33. .canonicalize()
  34. .context("failed to canonicalize dataflow path")?
  35. .parent()
  36. .ok_or_else(|| eyre::eyre!("canonicalized dataflow path has no parent"))?
  37. .to_owned();
  38. for node in nodes.into_values() {
  39. match node.kind {
  40. // Reloading Custom Nodes is not supported. See: https://github.com/dora-rs/dora/pull/239#discussion_r1154313139
  41. CoreNodeKind::Custom(_cn) => (),
  42. CoreNodeKind::Runtime(rn) => {
  43. for op in rn.operators.iter() {
  44. if let dora_core::descriptor::OperatorSource::Python(python_source) =
  45. &op.config.source
  46. {
  47. let path = resolve_path(&python_source.source, &working_dir)
  48. .wrap_err_with(|| {
  49. format!("failed to resolve node source `{}`", python_source.source)
  50. })?;
  51. node_path_lookup
  52. .insert(path, (dataflow_id, node.id.clone(), Some(op.id.clone())));
  53. }
  54. // Reloading non-python operator is not supported. See: https://github.com/dora-rs/dora/pull/239#discussion_r1154313139
  55. }
  56. }
  57. }
  58. }
  59. // Setup dataflow file watcher if reload option is set.
  60. let watcher_tx = tx.clone();
  61. let _watcher = if hot_reload {
  62. let hash = node_path_lookup.clone();
  63. let paths = hash.keys();
  64. let notifier = move |event| {
  65. if let Ok(NotifyEvent {
  66. paths,
  67. kind: EventKind::Modify(ModifyKind::Data(_data)),
  68. ..
  69. }) = event
  70. {
  71. for path in paths {
  72. if let Some((dataflow_id, node_id, operator_id)) = node_path_lookup.get(&path) {
  73. watcher_tx
  74. .send(AttachEvent::Control(ControlRequest::Reload {
  75. dataflow_id: *dataflow_id,
  76. node_id: node_id.clone(),
  77. operator_id: operator_id.clone(),
  78. }))
  79. .context("Could not send reload request to the cli loop")
  80. .unwrap();
  81. }
  82. }
  83. // TODO: Manage different file event
  84. }
  85. };
  86. let mut watcher = RecommendedWatcher::new(
  87. notifier,
  88. Config::default().with_poll_interval(Duration::from_secs(1)),
  89. )?;
  90. for path in paths {
  91. watcher.watch(path, RecursiveMode::Recursive)?;
  92. }
  93. Some(watcher)
  94. } else {
  95. None
  96. };
  97. // Setup Ctrlc Watcher to stop dataflow after ctrlc
  98. let ctrlc_tx = tx.clone();
  99. let mut ctrlc_sent = false;
  100. ctrlc::set_handler(move || {
  101. if ctrlc_sent {
  102. std::process::abort();
  103. } else {
  104. if ctrlc_tx
  105. .send(AttachEvent::Control(ControlRequest::Stop {
  106. dataflow_uuid: dataflow_id,
  107. grace_duration: None,
  108. }))
  109. .is_err()
  110. {
  111. // bail!("failed to report ctrl-c event to dora-daemon");
  112. }
  113. ctrlc_sent = true;
  114. }
  115. })
  116. .wrap_err("failed to set ctrl-c handler")?;
  117. // subscribe to log messages
  118. let mut log_session = TcpConnection {
  119. stream: TcpStream::connect(coordinator_socket)
  120. .wrap_err("failed to connect to dora coordinator")?,
  121. };
  122. log_session
  123. .send(
  124. &serde_json::to_vec(&ControlRequest::LogSubscribe {
  125. dataflow_id,
  126. level: log_level,
  127. })
  128. .wrap_err("failed to serialize message")?,
  129. )
  130. .wrap_err("failed to send log subscribe request to coordinator")?;
  131. std::thread::spawn(move || {
  132. while let Ok(raw) = log_session.receive() {
  133. let parsed: eyre::Result<LogMessage> =
  134. serde_json::from_slice(&raw).context("failed to parse log message");
  135. if tx.send(AttachEvent::Log(parsed)).is_err() {
  136. break;
  137. }
  138. }
  139. });
  140. loop {
  141. let control_request = match rx.recv_timeout(Duration::from_secs(1)) {
  142. Err(_err) => ControlRequest::Check {
  143. dataflow_uuid: dataflow_id,
  144. },
  145. Ok(AttachEvent::Control(control_request)) => control_request,
  146. Ok(AttachEvent::Log(Ok(log_message))) => {
  147. print_log_message(log_message, false, print_daemon_name);
  148. continue;
  149. }
  150. Ok(AttachEvent::Log(Err(err))) => {
  151. tracing::warn!("failed to parse log message: {:#?}", err);
  152. continue;
  153. }
  154. };
  155. let reply_raw = session
  156. .request(&serde_json::to_vec(&control_request)?)
  157. .wrap_err("failed to send request message to coordinator")?;
  158. let result: ControlRequestReply =
  159. serde_json::from_slice(&reply_raw).wrap_err("failed to parse reply")?;
  160. match result {
  161. ControlRequestReply::DataflowSpawned { uuid: _ } => (),
  162. ControlRequestReply::DataflowStopped { uuid, result } => {
  163. info!("dataflow {uuid} stopped");
  164. break handle_dataflow_result(result, Some(uuid));
  165. }
  166. ControlRequestReply::DataflowReloaded { uuid } => {
  167. info!("dataflow {uuid} reloaded")
  168. }
  169. other => error!("Received unexpected Coordinator Reply: {:#?}", other),
  170. };
  171. }
  172. }
  173. enum AttachEvent {
  174. Control(ControlRequest),
  175. Log(eyre::Result<LogMessage>),
  176. }