5
5
import shlex
6
6
import time
7
7
import traceback
8
+ import re
8
9
9
10
from supervisor .compat import maxint
10
11
from supervisor .compat import as_bytes
22
23
from supervisor .options import decode_wait_status
23
24
from supervisor .options import signame
24
25
from supervisor .options import ProcessException , BadCommand
26
+ from supervisor .options import readFile
25
27
26
28
from supervisor .dispatchers import EventListenerStates
27
29
@@ -197,79 +199,97 @@ def record_spawnerr(self, msg):
197
199
self .spawnerr = msg
198
200
self .config .options .logger .info ("spawnerr: %s" % msg )
199
201
200
- def spawn (self ):
202
+ def queue_all_dependee_processes (self , supervisor ):
203
+ if self .config .name not in supervisor .process_spawn_set :
204
+ supervisor .process_spawn_queue .append (self )
205
+ supervisor .process_spawn_set .add (self .config .name )
206
+ # all dependees that are not in queue and not in STARTING need to be added to queue.
207
+ if self .config .depends_on is not None :
208
+ for dependee in self .config .depends_on .values ():
209
+ if dependee .state is not ProcessStates .RUNNING :
210
+ ready_to_be_spawned = False
211
+ if dependee .state is not ProcessStates .STARTING :
212
+ if dependee .config .name not in supervisor .process_spawn_set :
213
+ supervisor .process_spawn_queue .append (dependee )
214
+ supervisor .process_spawn_set .add (dependee .config .name )
215
+ dependee .queue_all_dependee_processes (supervisor )
216
+
217
+
218
+ def spawn (self , supervisor = None ):
201
219
"""Start the subprocess. It must not be running already.
202
220
203
221
Return the process id. If the fork() call fails, return None.
204
222
"""
223
+ # spawn if all dependees are running - else add to queue if not in queue already
224
+ ready_to_be_spawned = True
205
225
206
- options = self .config .options
207
- processname = as_string (self .config .name )
226
+ options = self .config .options
227
+ processname = as_string (self .config .name )
208
228
209
- if self .pid :
210
- msg = 'process \' %s\' already running' % processname
211
- options .logger .warn (msg )
212
- return
229
+ if self .pid :
230
+ msg = 'process \' %s\' already running' % processname
231
+ options .logger .warn (msg )
232
+ return
213
233
214
- self .killing = False
215
- self .spawnerr = None
216
- self .exitstatus = None
217
- self .system_stop = False
218
- self .administrative_stop = False
234
+ self .killing = False
235
+ self .spawnerr = None
236
+ self .exitstatus = None
237
+ self .system_stop = False
238
+ self .administrative_stop = False
219
239
220
- self .laststart = time .time ()
240
+ self .laststart = time .time ()
221
241
222
- self ._assertInState (ProcessStates .EXITED , ProcessStates .FATAL ,
223
- ProcessStates .BACKOFF , ProcessStates .STOPPED )
242
+ self ._assertInState (ProcessStates .EXITED , ProcessStates .FATAL ,
243
+ ProcessStates .BACKOFF , ProcessStates .STOPPED )
224
244
225
- self .change_state (ProcessStates .STARTING )
245
+ self .change_state (ProcessStates .STARTING )
226
246
227
- try :
228
- filename , argv = self .get_execv_args ()
229
- except ProcessException as what :
230
- self .record_spawnerr (what .args [0 ])
231
- self ._assertInState (ProcessStates .STARTING )
232
- self .change_state (ProcessStates .BACKOFF )
233
- return
247
+ try :
248
+ filename , argv = self .get_execv_args ()
249
+ except ProcessException as what :
250
+ self .record_spawnerr (what .args [0 ])
251
+ self ._assertInState (ProcessStates .STARTING )
252
+ self .change_state (ProcessStates .BACKOFF )
253
+ return
234
254
235
- try :
236
- self .dispatchers , self .pipes = self .config .make_dispatchers (self )
237
- except (OSError , IOError ) as why :
238
- code = why .args [0 ]
239
- if code == errno .EMFILE :
240
- # too many file descriptors open
241
- msg = 'too many open files to spawn \' %s\' ' % processname
242
- else :
243
- msg = 'unknown error making dispatchers for \' %s\' : %s' % (
244
- processname , errno .errorcode .get (code , code ))
245
- self .record_spawnerr (msg )
246
- self ._assertInState (ProcessStates .STARTING )
247
- self .change_state (ProcessStates .BACKOFF )
248
- return
255
+ try :
256
+ self .dispatchers , self .pipes = self .config .make_dispatchers (self )
257
+ except (OSError , IOError ) as why :
258
+ code = why .args [0 ]
259
+ if code == errno .EMFILE :
260
+ # too many file descriptors open
261
+ msg = 'too many open files to spawn \' %s\' ' % processname
262
+ else :
263
+ msg = 'unknown error making dispatchers for \' %s\' : %s' % (
264
+ processname , errno .errorcode .get (code , code ))
265
+ self .record_spawnerr (msg )
266
+ self ._assertInState (ProcessStates .STARTING )
267
+ self .change_state (ProcessStates .BACKOFF )
268
+ return
249
269
250
- try :
251
- pid = options .fork ()
252
- except OSError as why :
253
- code = why .args [0 ]
254
- if code == errno .EAGAIN :
255
- # process table full
256
- msg = ('Too many processes in process table to spawn \' %s\' ' %
257
- processname )
258
- else :
259
- msg = 'unknown error during fork for \' %s\' : %s' % (
260
- processname , errno .errorcode .get (code , code ))
261
- self .record_spawnerr (msg )
262
- self ._assertInState (ProcessStates .STARTING )
263
- self .change_state (ProcessStates .BACKOFF )
264
- options .close_parent_pipes (self .pipes )
265
- options .close_child_pipes (self .pipes )
266
- return
270
+ try :
271
+ pid = options .fork ()
272
+ except OSError as why :
273
+ code = why .args [0 ]
274
+ if code == errno .EAGAIN :
275
+ # process table full
276
+ msg = ('Too many processes in process table to spawn \' %s\' ' %
277
+ processname )
278
+ else :
279
+ msg = 'unknown error during fork for \' %s\' : %s' % (
280
+ processname , errno .errorcode .get (code , code ))
281
+ self .record_spawnerr (msg )
282
+ self ._assertInState (ProcessStates .STARTING )
283
+ self .change_state (ProcessStates .BACKOFF )
284
+ options .close_parent_pipes (self .pipes )
285
+ options .close_child_pipes (self .pipes )
286
+ return
267
287
268
- if pid != 0 :
269
- return self ._spawn_as_parent (pid )
288
+ if pid != 0 :
289
+ return self ._spawn_as_parent (pid )
270
290
271
- else :
272
- return self ._spawn_as_child (filename , argv )
291
+ else :
292
+ return self ._spawn_as_child (filename , argv )
273
293
274
294
def _spawn_as_parent (self , pid ):
275
295
# Parent
@@ -671,11 +691,11 @@ def transition(self, supervisord_instance=None):
671
691
if self .config .autorestart :
672
692
if self .config .autorestart is RestartUnconditionally :
673
693
# EXITED -> STARTING
674
- self .spawn ()
694
+ self .spawn (supervisor )
675
695
else : # autorestart is RestartWhenExitUnexpected
676
696
if self .exitstatus not in self .config .exitcodes :
677
697
# EXITED -> STARTING
678
- self .spawn ()
698
+ self .spawn (supervisor )
679
699
elif state == ProcessStates .STOPPED and not self .laststart :
680
700
if self .config .autostart :
681
701
# STOPPED -> STARTING
@@ -691,11 +711,11 @@ def transition(self, supervisord_instance=None):
691
711
if self .backoff <= self .config .startretries :
692
712
if now > self .delay :
693
713
# BACKOFF -> STARTING
694
- self .spawn ()
714
+ self .spawn (supervisor )
695
715
696
716
processname = as_string (self .config .name )
697
717
if state == ProcessStates .STARTING :
698
- if now - self .laststart > self .config .startsecs :
718
+ if now - self .laststart > self .config .startsecs and not self . config . runningregex :
699
719
# STARTING -> RUNNING if the proc has started
700
720
# successfully and it has stayed up for at least
701
721
# proc.config.startsecs,
@@ -708,6 +728,27 @@ def transition(self, supervisord_instance=None):
708
728
'> than %s seconds (startsecs)' % self .config .startsecs )
709
729
logger .info ('success: %s %s' % (processname , msg ))
710
730
731
+ if self .config .runningregex :
732
+ logfile = getattr (self .config , 'stdout_logfile' )
733
+ logfile_as_str = as_string (readFile (logfile , self .log_offset , 0 ))
734
+
735
+ # delete ascii escape sequence and newlines with regular expression
736
+ ansi_escape = re .compile (r'(\x9B|\x1B\[)[0-?]*[ -\/]*[@-~]|\n' )
737
+ logfile_as_str = ansi_escape .sub ('' , logfile_as_str )
738
+
739
+ # STARTING -> RUNNING if the process has started
740
+ # successfully and the runningregex is met
741
+ if self .config .runningregex .match (logfile_as_str ):
742
+ self .delay = 0
743
+ self .backoff = 0
744
+ self ._assertInState (ProcessStates .STARTING )
745
+ self .change_state (ProcessStates .RUNNING )
746
+ msg = ('entered RUNNING state, found runningregex in stdout' )
747
+ logger .info ('success: %s %s' % (processname , msg ))
748
+
749
+
750
+
751
+
711
752
if state == ProcessStates .BACKOFF :
712
753
if self .backoff > self .config .startretries :
713
754
# BACKOFF -> FATAL if the proc has exceeded its number
0 commit comments