Function that take a deployment reporter and a deployment task and execute/synchronize them together The reporter is going to be executed in a separate thread and the task in the current thread. Reporter will not be executed while the task is running the pre_run and post_run_success methods. Only during the run method
(
mut deployment_reporter: impl DeploymentReporter<DeploymentResult = TaskRet, Logger = Log>,
mut long_task: impl DeploymentTask<Logger = Log, DeploymentResult = TaskRet>,
)
| 173 | // Reporter will not be executed while the task is running the pre_run and post_run_success methods. |
| 174 | // Only during the run method |
| 175 | pub fn execute_long_deployment<Log, TaskRet>( |
| 176 | mut deployment_reporter: impl DeploymentReporter<DeploymentResult = TaskRet, Logger = Log>, |
| 177 | mut long_task: impl DeploymentTask<Logger = Log, DeploymentResult = TaskRet>, |
| 178 | ) -> Result<(), Box<EngineError>> { |
| 179 | // stop the thread when the blocking task is done |
| 180 | let (tx, rx) = mpsc::channel(); |
| 181 | let deployment_start = Arc::new(Barrier::new(2)); |
| 182 | let mut state = deployment_reporter.new_state(); |
| 183 | |
| 184 | let logger = deployment_reporter.logger(); |
| 185 | |
| 186 | // 1. Execute pre_run and if it succeed, start the reporter thread |
| 187 | let deployment_result = match long_task.pre_run(logger) { |
| 188 | Err(err) => Err(err), |
| 189 | Ok(prerun_result) => { |
| 190 | // 2. Start the reporter thread in background |
| 191 | thread::scope(|th_scope| { |
| 192 | // monitor thread to notify user while the blocking task is executed |
| 193 | let thread_name = format!("reporter-of-{}", thread::current().name().unwrap_or("unknown-thread")); |
| 194 | let th_handle = thread::Builder::new().name(thread_name).spawn_scoped(th_scope, { |
| 195 | // Propagate the current span into the thread. This span is only used by tests |
| 196 | let current_span = tracing::Span::current(); |
| 197 | let deployment_start = deployment_start.clone(); |
| 198 | let deployment_reporter = &deployment_reporter; // to avoid moving the object into the thread |
| 199 | let state = &mut state; |
| 200 | |
| 201 | move || { |
| 202 | let _span = current_span.enter(); |
| 203 | |
| 204 | // Before the launch of the deployment |
| 205 | deployment_reporter.deployment_before_start(state); |
| 206 | |
| 207 | // Wait the start of the deployment |
| 208 | deployment_start.wait(); |
| 209 | |
| 210 | // Send deployment progress report every x secs |
| 211 | let report_frequency = deployment_reporter.report_frequency(); |
| 212 | loop { |
| 213 | match rx.recv_timeout(report_frequency) { |
| 214 | // Deployment is terminated, we received the result of the task |
| 215 | Ok(_) => break, |
| 216 | |
| 217 | // Deployment is still in progress |
| 218 | Err(RecvTimeoutError::Timeout) => deployment_reporter.deployment_in_progress(state), |
| 219 | |
| 220 | // Other side died without passing us the result ! this is a logical bug ! |
| 221 | Err(RecvTimeoutError::Disconnected) => { |
| 222 | panic!( |
| 223 | "Haven't received task deployment result, but otherside of the channel is dead !" |
| 224 | ); |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | }); |
| 230 | |
| 231 | // Wait for our watcher thread to be ready before starting |
| 232 | let _ = deployment_start.wait(); |