11use std:: collections:: BTreeSet ;
2+ use std:: ffi:: OsStr ;
23use std:: io:: Write ;
4+ use std:: os:: unix:: ffi:: OsStrExt ;
5+ use std:: process:: Command ;
36use std:: time:: { Duration , SystemTime } ;
47use tokio:: time:: sleep;
58
@@ -13,41 +16,88 @@ use crate::client::status::{Status, is_terminated};
1316use crate :: common:: arraydef:: IntArray ;
1417use crate :: common:: utils:: str:: pluralize;
1518use crate :: rpc_call;
16- use crate :: server:: event:: payload:: EventPayload ;
19+ use crate :: server:: event:: payload:: { EventPayload , TaskNotification } ;
1720use crate :: server:: job:: JobTaskCounters ;
1821use crate :: transfer:: connection:: ClientSession ;
1922use crate :: transfer:: messages:: {
2023 FromClientMessage , IdSelector , JobDetailRequest , JobInfo , JobInfoRequest , TaskIdSelector ,
2124 TaskSelector , TaskStatusSelector , ToClientMessage , WaitForJobsRequest ,
2225} ;
2326use colored:: Colorize ;
27+ use itertools:: Itertools ;
2428use tako:: { JobId , JobTaskCount , Set , TaskId } ;
2529
30+ fn process_on_notify ( program : & str , args : & [ String ] , notification : & TaskNotification ) {
31+ log:: info!(
32+ "Running on_notify callback: {} {:?}: {:?}" ,
33+ program,
34+ args,
35+ notification
36+ ) ;
37+ let mut child = match Command :: new ( program)
38+ . args ( args)
39+ . arg ( OsStr :: from_bytes ( & notification. message ) )
40+ . env ( "HQ_JOB_ID" , notification. task_id . job_id ( ) . to_string ( ) )
41+ . env ( "HQ_TASK_ID" , notification. task_id . job_task_id ( ) . to_string ( ) )
42+ . env ( "HQ_WORKER_ID" , notification. worker_id . to_string ( ) )
43+ . spawn ( )
44+ {
45+ Ok ( child) => child,
46+ Err ( e) => {
47+ log:: warn!( "Failed to run on_notify callback: {}" , e) ;
48+ return ;
49+ }
50+ } ;
51+ match child. wait ( ) {
52+ Ok ( s) => {
53+ if !s. success ( ) {
54+ log:: warn!( "on_notify callback finished with exit code: {}" , s) ;
55+ }
56+ }
57+ Err ( e) => {
58+ log:: warn!( "Failed to run on_notify callback: {}" , e) ;
59+ return ;
60+ }
61+ }
62+ }
63+
2664pub async fn wait_for_jobs (
2765 session : & mut ClientSession ,
2866 jobs : & [ JobInfo ] ,
2967 wait_for_close : bool ,
68+ on_notify : Option < & str > ,
3069) -> anyhow:: Result < ( ) > {
3170 let mut unfinished_jobs = Set :: new ( ) ;
3271 for job in jobs {
3372 if !is_terminated ( job) || ( wait_for_close && job. is_open ) {
3473 unfinished_jobs. insert ( job. id ) ;
3574 }
3675 }
76+ let on_notify_program_and_args =
77+ on_notify. map ( |s| shlex:: split ( & s) . unwrap_or_else ( || vec ! [ s. to_string( ) ] ) ) ;
3778 while !unfinished_jobs. is_empty ( ) {
3879 if let Some ( msg) = session. connection ( ) . receive ( ) . await {
3980 let msg = msg?;
4081 let job_id = match & msg {
41- ToClientMessage :: Event ( event) => match event. payload {
82+ ToClientMessage :: Event ( event) => match & event. payload {
4283 EventPayload :: JobCompleted ( job_id) => job_id,
4384 EventPayload :: JobIdle ( job_id) if !wait_for_close => job_id,
85+ EventPayload :: TaskNotify ( notification) => {
86+ let program_and_args = on_notify_program_and_args. as_ref ( ) . unwrap ( ) ;
87+ process_on_notify (
88+ & program_and_args[ 0 ] ,
89+ & program_and_args[ 1 ..] ,
90+ notification,
91+ ) ;
92+ continue ;
93+ }
4494 _ => continue ,
4595 } ,
4696 _ => {
4797 return Err ( anyhow:: anyhow!( "Unexpected message from server" ) ) ;
4898 }
4999 } ;
50- unfinished_jobs. remove ( & job_id) ;
100+ unfinished_jobs. remove ( job_id) ;
51101 } else {
52102 return Ok ( ( ) ) ;
53103 }
0 commit comments