test_process.py 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383
  1. import os
  2. import signal
  3. import time
  4. import unittest
  5. import sys
  6. import errno
  7. from supervisor.tests.base import DummyOptions
  8. from supervisor.tests.base import DummyPConfig
  9. from supervisor.tests.base import DummyProcess
  10. from supervisor.tests.base import DummyPGroupConfig
  11. from supervisor.tests.base import DummyDispatcher
  12. from supervisor.tests.base import DummyEvent
  13. class SubprocessTests(unittest.TestCase):
  14. def _getTargetClass(self):
  15. from supervisor.process import Subprocess
  16. return Subprocess
  17. def _makeOne(self, *arg, **kw):
  18. return self._getTargetClass()(*arg, **kw)
  19. def tearDown(self):
  20. from supervisor.events import clear
  21. clear()
  22. def test_getProcessStateDescription(self):
  23. from supervisor.states import ProcessStates
  24. from supervisor.process import getProcessStateDescription
  25. for statename, code in ProcessStates.__dict__.items():
  26. self.assertEqual(getProcessStateDescription(code), statename)
  27. def test_ctor(self):
  28. options = DummyOptions()
  29. config = DummyPConfig(options, 'cat', 'bin/cat',
  30. stdout_logfile='/tmp/temp123.log',
  31. stderr_logfile='/tmp/temp456.log')
  32. instance = self._makeOne(config)
  33. self.assertEqual(instance.config, config)
  34. self.assertEqual(instance.config.options, options)
  35. self.assertEqual(instance.laststart, 0)
  36. self.assertEqual(instance.pid, 0)
  37. self.assertEqual(instance.laststart, 0)
  38. self.assertEqual(instance.laststop, 0)
  39. self.assertEqual(instance.delay, 0)
  40. self.assertEqual(instance.administrative_stop, 0)
  41. self.assertEqual(instance.killing, 0)
  42. self.assertEqual(instance.backoff, 0)
  43. self.assertEqual(instance.pipes, {})
  44. self.assertEqual(instance.dispatchers, {})
  45. self.assertEqual(instance.spawnerr, None)
  46. def test_repr(self):
  47. options = DummyOptions()
  48. config = DummyPConfig(options, 'cat', 'bin/cat')
  49. instance = self._makeOne(config)
  50. s = repr(instance)
  51. self.assertTrue(s.startswith('<Subprocess at'))
  52. self.assertTrue(s.endswith('with name cat in state STOPPED>'))
  53. def test_reopenlogs(self):
  54. options = DummyOptions()
  55. config = DummyPConfig(options, 'test', '/test')
  56. instance = self._makeOne(config)
  57. instance.dispatchers = {0:DummyDispatcher(readable=True),
  58. 1:DummyDispatcher(writable=True)}
  59. instance.reopenlogs()
  60. self.assertEqual(instance.dispatchers[0].logs_reopened, True)
  61. self.assertEqual(instance.dispatchers[1].logs_reopened, False)
  62. def test_removelogs(self):
  63. options = DummyOptions()
  64. config = DummyPConfig(options, 'test', '/test')
  65. instance = self._makeOne(config)
  66. instance.dispatchers = {0:DummyDispatcher(readable=True),
  67. 1:DummyDispatcher(writable=True)}
  68. instance.removelogs()
  69. self.assertEqual(instance.dispatchers[0].logs_removed, True)
  70. self.assertEqual(instance.dispatchers[1].logs_removed, False)
  71. def test_drain(self):
  72. options = DummyOptions()
  73. config = DummyPConfig(options, 'test', '/test',
  74. stdout_logfile='/tmp/foo',
  75. stderr_logfile='/tmp/bar')
  76. instance = self._makeOne(config)
  77. instance.dispatchers = {0:DummyDispatcher(readable=True),
  78. 1:DummyDispatcher(writable=True)}
  79. instance.drain()
  80. self.assertTrue(instance.dispatchers[0].read_event_handled)
  81. self.assertTrue(instance.dispatchers[1].write_event_handled)
  82. def test_get_execv_args_abs_missing(self):
  83. options = DummyOptions()
  84. config = DummyPConfig(options, 'notthere', '/notthere')
  85. instance = self._makeOne(config)
  86. args = instance.get_execv_args()
  87. self.assertEqual(args, ('/notthere', ['/notthere']))
  88. def test_get_execv_args_abs_withquotes_missing(self):
  89. options = DummyOptions()
  90. config = DummyPConfig(options, 'notthere', '/notthere "an argument"')
  91. instance = self._makeOne(config)
  92. args = instance.get_execv_args()
  93. self.assertEqual(args, ('/notthere', ['/notthere', 'an argument']))
  94. def test_get_execv_args_rel_missing(self):
  95. options = DummyOptions()
  96. config = DummyPConfig(options, 'notthere', 'notthere')
  97. instance = self._makeOne(config)
  98. args = instance.get_execv_args()
  99. self.assertEqual(args, (None, ['notthere']))
  100. def test_get_execv_args_rel_withquotes_missing(self):
  101. options = DummyOptions()
  102. config = DummyPConfig(options, 'notthere', 'notthere "an argument"')
  103. instance = self._makeOne(config)
  104. args = instance.get_execv_args()
  105. self.assertEqual(args, (None, ['notthere', 'an argument']))
  106. def test_get_execv_args_abs(self):
  107. executable = '/bin/sh foo'
  108. options = DummyOptions()
  109. config = DummyPConfig(options, 'sh', executable)
  110. instance = self._makeOne(config)
  111. args = instance.get_execv_args()
  112. self.assertEqual(len(args), 2)
  113. self.assertEqual(args[0], '/bin/sh')
  114. self.assertEqual(args[1], ['/bin/sh', 'foo'])
  115. def test_get_execv_args_rel(self):
  116. executable = 'sh foo'
  117. options = DummyOptions()
  118. config = DummyPConfig(options, 'sh', executable)
  119. instance = self._makeOne(config)
  120. args = instance.get_execv_args()
  121. self.assertEqual(len(args), 2)
  122. self.assertEqual(args[0], '/bin/sh')
  123. self.assertEqual(args[1], ['sh', 'foo'])
  124. def test_record_spawnerr(self):
  125. options = DummyOptions()
  126. config = DummyPConfig(options, 'test', '/test')
  127. instance = self._makeOne(config)
  128. instance.record_spawnerr('foo')
  129. self.assertEqual(instance.spawnerr, 'foo')
  130. self.assertEqual(options.logger.data[0], 'spawnerr: foo')
  131. self.assertEqual(instance.backoff, 1)
  132. self.failUnless(instance.delay)
  133. def test_spawn_already_running(self):
  134. options = DummyOptions()
  135. config = DummyPConfig(options, 'sh', '/bin/sh')
  136. instance = self._makeOne(config)
  137. instance.pid = True
  138. from supervisor.states import ProcessStates
  139. instance.state = ProcessStates.RUNNING
  140. result = instance.spawn()
  141. self.assertEqual(result, None)
  142. self.assertEqual(options.logger.data[0], "process 'sh' already running")
  143. self.assertEqual(instance.state, ProcessStates.RUNNING)
  144. def test_spawn_fail_check_execv_args(self):
  145. options = DummyOptions()
  146. config = DummyPConfig(options, 'bad', '/bad/filename')
  147. instance = self._makeOne(config)
  148. from supervisor.states import ProcessStates
  149. instance.state = ProcessStates.BACKOFF
  150. from supervisor import events
  151. L = []
  152. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  153. result = instance.spawn()
  154. self.assertEqual(result, None)
  155. self.assertEqual(instance.spawnerr, 'bad filename')
  156. self.assertEqual(options.logger.data[0], "spawnerr: bad filename")
  157. self.failUnless(instance.delay)
  158. self.failUnless(instance.backoff)
  159. from supervisor.states import ProcessStates
  160. self.assertEqual(instance.state, ProcessStates.BACKOFF)
  161. self.assertEqual(L[0].__class__, events.StartingFromBackoffEvent)
  162. self.assertEqual(L[1].__class__, events.BackoffFromStartingEvent)
  163. def test_spawn_fail_make_pipes_emfile(self):
  164. options = DummyOptions()
  165. import errno
  166. options.make_pipes_error = errno.EMFILE
  167. config = DummyPConfig(options, 'good', '/good/filename')
  168. instance = self._makeOne(config)
  169. from supervisor.states import ProcessStates
  170. instance.state = ProcessStates.BACKOFF
  171. from supervisor import events
  172. L = []
  173. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  174. result = instance.spawn()
  175. self.assertEqual(result, None)
  176. self.assertEqual(instance.spawnerr,
  177. "too many open files to spawn 'good'")
  178. self.assertEqual(options.logger.data[0],
  179. "spawnerr: too many open files to spawn 'good'")
  180. self.failUnless(instance.delay)
  181. self.failUnless(instance.backoff)
  182. from supervisor.states import ProcessStates
  183. self.assertEqual(instance.state, ProcessStates.BACKOFF)
  184. self.assertEqual(L[0].__class__, events.StartingFromBackoffEvent)
  185. self.assertEqual(L[1].__class__, events.BackoffFromStartingEvent)
  186. def test_spawn_fail_make_pipes_other(self):
  187. options = DummyOptions()
  188. options.make_pipes_error = 1
  189. config = DummyPConfig(options, 'good', '/good/filename')
  190. instance = self._makeOne(config)
  191. from supervisor.states import ProcessStates
  192. instance.state = ProcessStates.BACKOFF
  193. from supervisor import events
  194. L = []
  195. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  196. result = instance.spawn()
  197. self.assertEqual(result, None)
  198. self.assertEqual(instance.spawnerr, 'unknown error: EPERM')
  199. self.assertEqual(options.logger.data[0],
  200. "spawnerr: unknown error: EPERM")
  201. self.failUnless(instance.delay)
  202. self.failUnless(instance.backoff)
  203. from supervisor.states import ProcessStates
  204. self.assertEqual(instance.state, ProcessStates.BACKOFF)
  205. self.assertEqual(L[0].__class__, events.StartingFromBackoffEvent)
  206. self.assertEqual(L[1].__class__, events.BackoffFromStartingEvent)
  207. def test_spawn_fork_fail_eagain(self):
  208. options = DummyOptions()
  209. import errno
  210. options.fork_error = errno.EAGAIN
  211. config = DummyPConfig(options, 'good', '/good/filename')
  212. instance = self._makeOne(config)
  213. from supervisor.states import ProcessStates
  214. instance.state = ProcessStates.BACKOFF
  215. from supervisor import events
  216. L = []
  217. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  218. result = instance.spawn()
  219. self.assertEqual(result, None)
  220. self.assertEqual(instance.spawnerr,
  221. "Too many processes in process table to spawn 'good'")
  222. self.assertEqual(options.logger.data[0],
  223. "spawnerr: Too many processes in process table to spawn 'good'")
  224. self.assertEqual(len(options.parent_pipes_closed), 6)
  225. self.assertEqual(len(options.child_pipes_closed), 6)
  226. self.failUnless(instance.delay)
  227. self.failUnless(instance.backoff)
  228. from supervisor.states import ProcessStates
  229. self.assertEqual(instance.state, ProcessStates.BACKOFF)
  230. self.assertEqual(L[0].__class__, events.StartingFromBackoffEvent)
  231. self.assertEqual(L[1].__class__, events.BackoffFromStartingEvent)
  232. def test_spawn_fork_fail_other(self):
  233. options = DummyOptions()
  234. options.fork_error = 1
  235. config = DummyPConfig(options, 'good', '/good/filename')
  236. instance = self._makeOne(config)
  237. from supervisor.states import ProcessStates
  238. instance.state = ProcessStates.BACKOFF
  239. from supervisor import events
  240. L = []
  241. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  242. result = instance.spawn()
  243. self.assertEqual(result, None)
  244. self.assertEqual(instance.spawnerr, 'unknown error: EPERM')
  245. self.assertEqual(options.logger.data[0],
  246. "spawnerr: unknown error: EPERM")
  247. self.assertEqual(len(options.parent_pipes_closed), 6)
  248. self.assertEqual(len(options.child_pipes_closed), 6)
  249. self.failUnless(instance.delay)
  250. self.failUnless(instance.backoff)
  251. from supervisor.states import ProcessStates
  252. self.assertEqual(instance.state, ProcessStates.BACKOFF)
  253. self.assertEqual(L[0].__class__, events.StartingFromBackoffEvent)
  254. self.assertEqual(L[1].__class__, events.BackoffFromStartingEvent)
  255. def test_spawn_as_child_setuid_ok(self):
  256. options = DummyOptions()
  257. options.forkpid = 0
  258. config = DummyPConfig(options, 'good', '/good/filename', uid=1)
  259. instance = self._makeOne(config)
  260. result = instance.spawn()
  261. self.assertEqual(result, None)
  262. self.assertEqual(options.parent_pipes_closed, None)
  263. self.assertEqual(options.child_pipes_closed, None)
  264. self.assertEqual(options.pgrp_set, True)
  265. self.assertEqual(len(options.duped), 3)
  266. self.assertEqual(len(options.fds_closed), options.minfds - 3)
  267. self.assertEqual(options.written, {})
  268. self.assertEqual(options.privsdropped, 1)
  269. self.assertEqual(options.execv_args,
  270. ('/good/filename', ['/good/filename']) )
  271. self.assertEqual(options._exitcode, 127)
  272. def test_spawn_as_child_setuid_fail(self):
  273. options = DummyOptions()
  274. options.forkpid = 0
  275. options.setuid_msg = 'screwed'
  276. config = DummyPConfig(options, 'good', '/good/filename', uid=1)
  277. instance = self._makeOne(config)
  278. result = instance.spawn()
  279. self.assertEqual(result, None)
  280. self.assertEqual(options.parent_pipes_closed, None)
  281. self.assertEqual(options.child_pipes_closed, None)
  282. self.assertEqual(options.pgrp_set, True)
  283. self.assertEqual(len(options.duped), 3)
  284. self.assertEqual(len(options.fds_closed), options.minfds - 3)
  285. self.assertEqual(options.written,
  286. {2: 'supervisor: error trying to setuid to 1 (screwed)\n'})
  287. self.assertEqual(options.privsdropped, None)
  288. self.assertEqual(options.execv_args,
  289. ('/good/filename', ['/good/filename']) )
  290. self.assertEqual(options._exitcode, 127)
  291. def test_spawn_as_child_execv_fail_oserror(self):
  292. options = DummyOptions()
  293. options.forkpid = 0
  294. options.execv_error = 1
  295. config = DummyPConfig(options, 'good', '/good/filename')
  296. instance = self._makeOne(config)
  297. result = instance.spawn()
  298. self.assertEqual(result, None)
  299. self.assertEqual(options.parent_pipes_closed, None)
  300. self.assertEqual(options.child_pipes_closed, None)
  301. self.assertEqual(options.pgrp_set, True)
  302. self.assertEqual(len(options.duped), 3)
  303. self.assertEqual(len(options.fds_closed), options.minfds - 3)
  304. self.assertEqual(options.written,
  305. {2: "couldn't exec /good/filename: EPERM\n"})
  306. self.assertEqual(options.privsdropped, None)
  307. self.assertEqual(options._exitcode, 127)
  308. def test_spawn_as_child_execv_fail_runtime_error(self):
  309. options = DummyOptions()
  310. options.forkpid = 0
  311. options.execv_error = 2
  312. config = DummyPConfig(options, 'good', '/good/filename')
  313. instance = self._makeOne(config)
  314. result = instance.spawn()
  315. self.assertEqual(result, None)
  316. self.assertEqual(options.parent_pipes_closed, None)
  317. self.assertEqual(options.child_pipes_closed, None)
  318. self.assertEqual(options.pgrp_set, True)
  319. self.assertEqual(len(options.duped), 3)
  320. self.assertEqual(len(options.fds_closed), options.minfds - 3)
  321. msg = options.written[2] # dict, 2 is fd #
  322. self.failUnless(msg.startswith("couldn't exec /good/filename:"))
  323. self.failUnless("exceptions.RuntimeError" in msg)
  324. self.assertEqual(options.privsdropped, None)
  325. self.assertEqual(options._exitcode, 127)
  326. def test_spawn_as_child_uses_pconfig_environment(self):
  327. options = DummyOptions()
  328. options.forkpid = 0
  329. config = DummyPConfig(options, 'cat', '/bin/cat',
  330. environment={'_TEST_':'1'})
  331. instance = self._makeOne(config)
  332. result = instance.spawn()
  333. self.assertEqual(result, None)
  334. self.assertEqual(options.execv_args, ('/bin/cat', ['/bin/cat']) )
  335. self.assertEqual(options.execv_environment['_TEST_'], '1')
  336. def test_spawn_as_child_environment_supervisor_process_name(self):
  337. options = DummyOptions()
  338. options.forkpid = 0
  339. config = DummyPConfig(options, 'cat', '/bin/cat')
  340. instance = self._makeOne(config)
  341. class Dummy:
  342. name = 'dummy'
  343. instance.group = Dummy()
  344. instance.group.config = Dummy()
  345. result = instance.spawn()
  346. self.assertEqual(result, None)
  347. self.assertEqual(options.execv_args, ('/bin/cat', ['/bin/cat']) )
  348. self.assertEqual(
  349. options.execv_environment['SUPERVISOR_PROCESS_NAME'], 'cat')
  350. self.assertEqual(
  351. options.execv_environment['SUPERVISOR_GROUP_NAME'], 'dummy')
  352. def test_spawn_as_child_stderr_redirected(self):
  353. options = DummyOptions()
  354. options.forkpid = 0
  355. config = DummyPConfig(options, 'good', '/good/filename', uid=1)
  356. config.redirect_stderr = True
  357. instance = self._makeOne(config)
  358. result = instance.spawn()
  359. self.assertEqual(result, None)
  360. self.assertEqual(options.parent_pipes_closed, None)
  361. self.assertEqual(options.child_pipes_closed, None)
  362. self.assertEqual(options.pgrp_set, True)
  363. self.assertEqual(len(options.duped), 2)
  364. self.assertEqual(len(options.fds_closed), options.minfds - 3)
  365. self.assertEqual(options.written, {})
  366. self.assertEqual(options.privsdropped, 1)
  367. self.assertEqual(options.execv_args,
  368. ('/good/filename', ['/good/filename']) )
  369. self.assertEqual(options._exitcode, 127)
  370. def test_spawn_as_parent(self):
  371. options = DummyOptions()
  372. options.forkpid = 10
  373. config = DummyPConfig(options, 'good', '/good/filename')
  374. instance = self._makeOne(config)
  375. result = instance.spawn()
  376. self.assertEqual(result, 10)
  377. self.assertEqual(instance.dispatchers[4].__class__, DummyDispatcher)
  378. self.assertEqual(instance.dispatchers[5].__class__, DummyDispatcher)
  379. self.assertEqual(instance.dispatchers[7].__class__, DummyDispatcher)
  380. self.assertEqual(instance.pipes['stdin'], 4)
  381. self.assertEqual(instance.pipes['stdout'], 5)
  382. self.assertEqual(instance.pipes['stderr'], 7)
  383. self.assertEqual(options.parent_pipes_closed, None)
  384. self.assertEqual(len(options.child_pipes_closed), 6)
  385. self.assertEqual(options.logger.data[0], "spawned: 'good' with pid 10")
  386. self.assertEqual(instance.spawnerr, None)
  387. self.failUnless(instance.delay)
  388. self.assertEqual(instance.config.options.pidhistory[10], instance)
  389. from supervisor.states import ProcessStates
  390. self.assertEqual(instance.state, ProcessStates.STARTING)
  391. def test_spawn_redirect_stderr(self):
  392. options = DummyOptions()
  393. options.forkpid = 10
  394. config = DummyPConfig(options, 'good', '/good/filename',
  395. redirect_stderr=True)
  396. instance = self._makeOne(config)
  397. result = instance.spawn()
  398. self.assertEqual(result, 10)
  399. self.assertEqual(instance.dispatchers[4].__class__, DummyDispatcher)
  400. self.assertEqual(instance.dispatchers[5].__class__, DummyDispatcher)
  401. self.assertEqual(instance.pipes['stdin'], 4)
  402. self.assertEqual(instance.pipes['stdout'], 5)
  403. self.assertEqual(instance.pipes['stderr'], None)
  404. def test_write(self):
  405. executable = '/bin/cat'
  406. options = DummyOptions()
  407. config = DummyPConfig(options, 'output', executable)
  408. instance = self._makeOne(config)
  409. sent = 'a' * (1 << 13)
  410. self.assertRaises(OSError, instance.write, sent)
  411. options.forkpid = 1
  412. result = instance.spawn()
  413. instance.write(sent)
  414. stdin_fd = instance.pipes['stdin']
  415. self.assertEqual(sent, instance.dispatchers[stdin_fd].input_buffer)
  416. instance.killing = True
  417. self.assertRaises(OSError, instance.write, sent)
  418. def test_write_dispatcher_closed(self):
  419. executable = '/bin/cat'
  420. options = DummyOptions()
  421. config = DummyPConfig(options, 'output', executable)
  422. instance = self._makeOne(config)
  423. sent = 'a' * (1 << 13)
  424. self.assertRaises(OSError, instance.write, sent)
  425. options.forkpid = 1
  426. result = instance.spawn()
  427. stdin_fd = instance.pipes['stdin']
  428. instance.dispatchers[stdin_fd].close()
  429. self.assertRaises(OSError, instance.write, sent)
  430. def test_write_dispatcher_flush_raises_epipe(self):
  431. executable = '/bin/cat'
  432. options = DummyOptions()
  433. config = DummyPConfig(options, 'output', executable)
  434. instance = self._makeOne(config)
  435. sent = 'a' * (1 << 13)
  436. self.assertRaises(OSError, instance.write, sent)
  437. options.forkpid = 1
  438. result = instance.spawn()
  439. stdin_fd = instance.pipes['stdin']
  440. instance.dispatchers[stdin_fd].flush_error = errno.EPIPE
  441. self.assertRaises(OSError, instance.write, sent)
  442. def dont_test_spawn_and_kill(self):
  443. # this is a functional test
  444. from supervisor.tests.base import makeSpew
  445. try:
  446. called = 0
  447. def foo(*args):
  448. called = 1
  449. signal.signal(signal.SIGCHLD, foo)
  450. executable = makeSpew()
  451. options = DummyOptions()
  452. config = DummyPConfig(options, 'spew', executable)
  453. instance = self._makeOne(config)
  454. result = instance.spawn()
  455. msg = options.logger.data[0]
  456. self.failUnless(msg.startswith("spawned: 'spew' with pid"))
  457. self.assertEqual(len(instance.pipes), 6)
  458. self.failUnless(instance.pid)
  459. self.failUnlessEqual(instance.pid, result)
  460. origpid = instance.pid
  461. import errno
  462. while 1:
  463. try:
  464. data = os.popen('ps').read()
  465. break
  466. except IOError, why:
  467. if why[0] != errno.EINTR:
  468. raise
  469. # try again ;-)
  470. time.sleep(0.1) # arbitrary, race condition possible
  471. self.failUnless(data.find(`origpid`) != -1 )
  472. msg = instance.kill(signal.SIGTERM)
  473. time.sleep(0.1) # arbitrary, race condition possible
  474. self.assertEqual(msg, None)
  475. pid, sts = os.waitpid(-1, os.WNOHANG)
  476. data = os.popen('ps').read()
  477. self.assertEqual(data.find(`origpid`), -1) # dubious
  478. finally:
  479. try:
  480. os.remove(executable)
  481. except:
  482. pass
  483. signal.signal(signal.SIGCHLD, signal.SIG_DFL)
  484. def test_stop(self):
  485. options = DummyOptions()
  486. config = DummyPConfig(options, 'test', '/test')
  487. instance = self._makeOne(config)
  488. instance.pid = 11
  489. dispatcher = DummyDispatcher(writable=True)
  490. instance.dispatchers = {'foo':dispatcher}
  491. from supervisor.states import ProcessStates
  492. instance.state = ProcessStates.RUNNING
  493. instance.stop()
  494. self.assertEqual(instance.administrative_stop, 1)
  495. self.failUnless(instance.delay)
  496. self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
  497. 'signal SIGTERM')
  498. self.assertEqual(instance.killing, 1)
  499. self.assertEqual(options.kills[11], signal.SIGTERM)
  500. self.assertEqual(dispatcher.write_event_handled, True)
  501. def test_give_up(self):
  502. options = DummyOptions()
  503. config = DummyPConfig(options, 'test', '/test')
  504. instance = self._makeOne(config)
  505. L = []
  506. from supervisor.states import ProcessStates
  507. from supervisor import events
  508. events.subscribe(events.FatalFromBackoffEvent, lambda x: L.append(x))
  509. instance.state = ProcessStates.BACKOFF
  510. instance.give_up()
  511. self.assertEqual(instance.system_stop, 1)
  512. self.assertFalse(instance.delay)
  513. self.assertFalse(instance.backoff)
  514. self.assertEqual(instance.state, ProcessStates.FATAL)
  515. self.assertEqual(L[0].__class__, events.FatalFromBackoffEvent)
  516. def test_kill_nopid(self):
  517. options = DummyOptions()
  518. config = DummyPConfig(options, 'test', '/test')
  519. instance = self._makeOne(config)
  520. instance.kill(signal.SIGTERM)
  521. self.assertEqual(options.logger.data[0],
  522. 'attempted to kill test with sig SIGTERM but it wasn\'t running')
  523. self.assertEqual(instance.killing, 0)
  524. def test_kill_error(self):
  525. options = DummyOptions()
  526. config = DummyPConfig(options, 'test', '/test')
  527. options.kill_error = 1
  528. instance = self._makeOne(config)
  529. L = []
  530. from supervisor.states import ProcessStates
  531. from supervisor import events
  532. events.subscribe(events.ProcessStateChangeEvent,
  533. lambda x: L.append(x))
  534. instance.pid = 11
  535. instance.state = ProcessStates.RUNNING
  536. instance.kill(signal.SIGTERM)
  537. self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
  538. 'signal SIGTERM')
  539. self.failUnless(options.logger.data[1].startswith(
  540. 'unknown problem killing test'))
  541. self.assertEqual(instance.killing, 0)
  542. self.assertEqual(L[0].__class__, events.StoppingFromRunningEvent)
  543. self.assertEqual(L[1].__class__, events.ToUnknownEvent)
  544. def test_kill_from_starting(self):
  545. options = DummyOptions()
  546. config = DummyPConfig(options, 'test', '/test')
  547. instance = self._makeOne(config)
  548. instance.pid = 11
  549. L = []
  550. from supervisor.states import ProcessStates
  551. from supervisor import events
  552. events.subscribe(events.StoppingFromStartingEvent,lambda x: L.append(x))
  553. from supervisor.states import ProcessStates
  554. instance.state = ProcessStates.STARTING
  555. instance.kill(signal.SIGTERM)
  556. self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
  557. 'signal SIGTERM')
  558. self.assertEqual(instance.killing, 1)
  559. self.assertEqual(options.kills[11], signal.SIGTERM)
  560. self.assertEqual(L[0].__class__, events.StoppingFromStartingEvent)
  561. def test_kill_from_running(self):
  562. options = DummyOptions()
  563. config = DummyPConfig(options, 'test', '/test')
  564. instance = self._makeOne(config)
  565. instance.pid = 11
  566. L = []
  567. from supervisor.states import ProcessStates
  568. from supervisor import events
  569. events.subscribe(events.StoppingFromRunningEvent, lambda x: L.append(x))
  570. from supervisor.states import ProcessStates
  571. instance.state = ProcessStates.RUNNING
  572. instance.kill(signal.SIGTERM)
  573. self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
  574. 'signal SIGTERM')
  575. self.assertEqual(instance.killing, 1)
  576. self.assertEqual(options.kills[11], signal.SIGTERM)
  577. self.assertEqual(L[0].__class__, events.StoppingFromRunningEvent)
  578. def test_kill_from_stopping(self):
  579. options = DummyOptions()
  580. config = DummyPConfig(options, 'test', '/test')
  581. instance = self._makeOne(config)
  582. instance.pid = 11
  583. L = []
  584. from supervisor.states import ProcessStates
  585. from supervisor import events
  586. events.subscribe(events.Event,lambda x: L.append(x))
  587. from supervisor.states import ProcessStates
  588. instance.state = ProcessStates.STOPPING
  589. instance.kill(signal.SIGKILL)
  590. self.assertEqual(options.logger.data[0], 'killing test (pid 11) with '
  591. 'signal SIGKILL')
  592. self.assertEqual(instance.killing, 1)
  593. self.assertEqual(options.kills[11], signal.SIGKILL)
  594. self.assertEqual(L, []) # no event because we didn't change state
  595. def test_finish(self):
  596. options = DummyOptions()
  597. config = DummyPConfig(options, 'notthere', '/notthere',
  598. stdout_logfile='/tmp/foo')
  599. instance = self._makeOne(config)
  600. instance.waitstatus = (123, 1) # pid, waitstatus
  601. instance.config.options.pidhistory[123] = instance
  602. instance.killing = 1
  603. pipes = {'stdout':'','stderr':''}
  604. instance.pipes = pipes
  605. from supervisor.states import ProcessStates
  606. from supervisor import events
  607. instance.state = ProcessStates.STOPPING
  608. L = []
  609. events.subscribe(events.StoppedFromStoppingEvent, lambda x: L.append(x))
  610. instance.finish(123, 1)
  611. self.assertEqual(instance.killing, 0)
  612. self.assertEqual(instance.pid, 0)
  613. self.assertEqual(options.parent_pipes_closed, pipes)
  614. self.assertEqual(instance.pipes, {})
  615. self.assertEqual(instance.dispatchers, {})
  616. self.assertEqual(options.logger.data[0], 'stopped: notthere '
  617. '(terminated by SIGHUP)')
  618. self.assertEqual(instance.exitstatus, -1)
  619. self.assertEqual(L[0].__class__, events.StoppedFromStoppingEvent)
  620. def test_finish_expected(self):
  621. options = DummyOptions()
  622. config = DummyPConfig(options, 'notthere', '/notthere',
  623. stdout_logfile='/tmp/foo')
  624. instance = self._makeOne(config)
  625. instance.config.options.pidhistory[123] = instance
  626. pipes = {'stdout':'','stderr':''}
  627. instance.pipes = pipes
  628. instance.config.exitcodes =[-1]
  629. from supervisor.states import ProcessStates
  630. from supervisor import events
  631. instance.state = ProcessStates.RUNNING
  632. L = []
  633. events.subscribe(events.ExitedFromRunningEvent, lambda x: L.append(x))
  634. instance.finish(123, 1)
  635. self.assertEqual(instance.killing, 0)
  636. self.assertEqual(instance.pid, 0)
  637. self.assertEqual(options.parent_pipes_closed, pipes)
  638. self.assertEqual(instance.pipes, {})
  639. self.assertEqual(instance.dispatchers, {})
  640. self.assertEqual(options.logger.data[0],
  641. 'exited: notthere (terminated by SIGHUP; expected)')
  642. self.assertEqual(instance.exitstatus, -1)
  643. self.assertEqual(L[0].__class__, events.ExitedFromRunningEvent)
  644. def test_finish_unexpected(self):
  645. options = DummyOptions()
  646. config = DummyPConfig(options, 'notthere', '/notthere',
  647. stdout_logfile='/tmp/foo', startsecs=10)
  648. instance = self._makeOne(config)
  649. instance.config.options.pidhistory[123] = instance
  650. pipes = {'stdout':'','stderr':''}
  651. instance.pipes = pipes
  652. instance.config.exitcodes =[-1]
  653. import time
  654. instance.laststart = time.time()
  655. from supervisor.states import ProcessStates
  656. from supervisor import events
  657. instance.state = ProcessStates.STARTING
  658. L = []
  659. events.subscribe(events.BackoffFromStartingEvent, lambda x: L.append(x))
  660. instance.finish(123, 1)
  661. self.assertEqual(instance.killing, 0)
  662. self.assertEqual(instance.pid, 0)
  663. self.assertEqual(options.parent_pipes_closed, pipes)
  664. self.assertEqual(instance.pipes, {})
  665. self.assertEqual(instance.dispatchers, {})
  666. self.assertEqual(options.logger.data[0],
  667. 'exited: notthere (terminated by SIGHUP; not expected)')
  668. self.assertEqual(instance.exitstatus, None)
  669. self.assertEqual(L[0].__class__, events.BackoffFromStartingEvent)
  670. def test_finish_with_current_event_sends_rejected(self):
  671. from supervisor import events
  672. L = []
  673. events.subscribe(events.ExitedFromRunningEvent, lambda x: L.append(x))
  674. events.subscribe(events.EventRejectedEvent, lambda x: L.append(x))
  675. options = DummyOptions()
  676. config = DummyPConfig(options, 'notthere', '/notthere',
  677. stdout_logfile='/tmp/foo', startsecs=10)
  678. instance = self._makeOne(config)
  679. from supervisor.states import ProcessStates
  680. instance.state = ProcessStates.RUNNING
  681. event = DummyEvent()
  682. instance.event = event
  683. instance.finish(123, 1)
  684. self.assertEqual(L[0].__class__, events.ExitedFromRunningEvent)
  685. self.assertEqual(L[1].__class__, events.EventRejectedEvent)
  686. self.assertEqual(L[1].process, instance)
  687. self.assertEqual(L[1].event, event)
  688. self.assertEqual(instance.event, None)
  689. def test_set_uid_no_uid(self):
  690. options = DummyOptions()
  691. config = DummyPConfig(options, 'test', '/test')
  692. instance = self._makeOne(config)
  693. instance.set_uid()
  694. self.assertEqual(options.privsdropped, None)
  695. def test_set_uid(self):
  696. options = DummyOptions()
  697. config = DummyPConfig(options, 'test', '/test', uid=1)
  698. instance = self._makeOne(config)
  699. msg = instance.set_uid()
  700. self.assertEqual(options.privsdropped, 1)
  701. self.assertEqual(msg, None)
  702. def test_cmp_bypriority(self):
  703. options = DummyOptions()
  704. config = DummyPConfig(options, 'notthere', '/notthere',
  705. stdout_logfile='/tmp/foo',
  706. priority=1)
  707. instance = self._makeOne(config)
  708. config = DummyPConfig(options, 'notthere1', '/notthere',
  709. stdout_logfile='/tmp/foo',
  710. priority=2)
  711. instance1 = self._makeOne(config)
  712. config = DummyPConfig(options, 'notthere2', '/notthere',
  713. stdout_logfile='/tmp/foo',
  714. priority=3)
  715. instance2 = self._makeOne(config)
  716. L = [instance2, instance, instance1]
  717. L.sort()
  718. self.assertEqual(L, [instance, instance1, instance2])
  719. def test_transition_stopped_to_starting_supervisor_stopping(self):
  720. from supervisor import events
  721. L = []
  722. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  723. from supervisor.states import ProcessStates, SupervisorStates
  724. options = DummyOptions()
  725. options.mood = SupervisorStates.SHUTDOWN
  726. # this should not be spawned, as supervisor is shutting down
  727. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  728. process = self._makeOne(pconfig)
  729. process.laststart = 0
  730. process.state = ProcessStates.STOPPED
  731. process.transition()
  732. self.assertEqual(process.state, ProcessStates.STOPPED)
  733. self.assertEqual(L, [])
  734. def test_transition_stopped_to_starting_supervisor_running(self):
  735. from supervisor import events
  736. L = []
  737. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  738. from supervisor.states import ProcessStates, SupervisorStates
  739. options = DummyOptions()
  740. options.mood = SupervisorStates.RUNNING
  741. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  742. process = self._makeOne(pconfig)
  743. process.laststart = 0
  744. process.state = ProcessStates.STOPPED
  745. process.transition()
  746. self.assertEqual(process.state, ProcessStates.STARTING)
  747. self.assertEqual(L[0].__class__, events.StartingFromStoppedEvent)
  748. def test_transition_exited_to_starting_supervisor_stopping(self):
  749. from supervisor import events
  750. L = []
  751. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  752. from supervisor.states import ProcessStates, SupervisorStates
  753. options = DummyOptions()
  754. options.mood = SupervisorStates.SHUTDOWN
  755. # this should not be spawned, as supervisor is shutting down
  756. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  757. from supervisor.datatypes import RestartUnconditionally
  758. pconfig.autorestart = RestartUnconditionally
  759. process = self._makeOne(pconfig)
  760. process.laststart = 1
  761. process.system_stop = 1
  762. process.state = ProcessStates.EXITED
  763. process.transition()
  764. self.assertEqual(process.state, ProcessStates.EXITED)
  765. self.assertEqual(process.system_stop, 1)
  766. self.assertEqual(L, [])
  767. def test_transition_exited_to_starting_uncond_supervisor_running(self):
  768. from supervisor import events
  769. L = []
  770. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  771. from supervisor.states import ProcessStates, SupervisorStates
  772. options = DummyOptions()
  773. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  774. from supervisor.datatypes import RestartUnconditionally
  775. pconfig.autorestart = RestartUnconditionally
  776. process = self._makeOne(pconfig)
  777. process.laststart = 1
  778. process.state = ProcessStates.EXITED
  779. process.transition()
  780. self.assertEqual(process.state, ProcessStates.STARTING)
  781. self.assertEqual(L[0].__class__, events.StartingFromExitedEvent)
  782. def test_transition_exited_to_starting_condit_supervisor_running(self):
  783. from supervisor import events
  784. L = []
  785. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  786. from supervisor.states import ProcessStates, SupervisorStates
  787. options = DummyOptions()
  788. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  789. from supervisor.datatypes import RestartWhenExitUnexpected
  790. pconfig.autorestart = RestartWhenExitUnexpected
  791. process = self._makeOne(pconfig)
  792. process.laststart = 1
  793. process.state = ProcessStates.EXITED
  794. process.exitstatus = 'bogus'
  795. process.transition()
  796. self.assertEqual(process.state, ProcessStates.STARTING)
  797. self.assertEqual(L[0].__class__, events.StartingFromExitedEvent)
  798. def test_transition_exited_to_starting_condit_fls_supervisor_running(self):
  799. from supervisor import events
  800. L = []
  801. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  802. from supervisor.states import ProcessStates, SupervisorStates
  803. options = DummyOptions()
  804. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  805. from supervisor.datatypes import RestartWhenExitUnexpected
  806. pconfig.autorestart = RestartWhenExitUnexpected
  807. process = self._makeOne(pconfig)
  808. process.laststart = 1
  809. process.state = ProcessStates.EXITED
  810. process.exitstatus = 0
  811. process.transition()
  812. self.assertEqual(process.state, ProcessStates.EXITED)
  813. self.assertEqual(L, [])
  814. def test_transition_backoff_to_starting_supervisor_stopping(self):
  815. from supervisor import events
  816. L = []
  817. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  818. from supervisor.states import ProcessStates, SupervisorStates
  819. options = DummyOptions()
  820. options.mood = SupervisorStates.SHUTDOWN
  821. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  822. process = self._makeOne(pconfig)
  823. process.laststart = 1
  824. process.delay = 0
  825. process.backoff = 0
  826. process.state = ProcessStates.BACKOFF
  827. process.transition()
  828. self.assertEqual(process.state, ProcessStates.BACKOFF)
  829. self.assertEqual(L, [])
  830. def test_transition_backoff_to_starting_supervisor_running(self):
  831. from supervisor import events
  832. L = []
  833. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  834. from supervisor.states import ProcessStates, SupervisorStates
  835. options = DummyOptions()
  836. options.mood = SupervisorStates.RUNNING
  837. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  838. process = self._makeOne(pconfig)
  839. process.laststart = 1
  840. process.delay = 0
  841. process.backoff = 0
  842. process.state = ProcessStates.BACKOFF
  843. process.transition()
  844. self.assertEqual(process.state, ProcessStates.STARTING)
  845. self.assertEqual(L[0].__class__, events.StartingFromBackoffEvent)
  846. def test_transition_backoff_to_starting_supervisor_running_notyet(self):
  847. from supervisor import events
  848. L = []
  849. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  850. from supervisor.states import ProcessStates, SupervisorStates
  851. options = DummyOptions()
  852. options.mood = SupervisorStates.RUNNING
  853. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  854. process = self._makeOne(pconfig)
  855. process.laststart = 1
  856. process.delay = sys.maxint
  857. process.backoff = 0
  858. process.state = ProcessStates.BACKOFF
  859. process.transition()
  860. self.assertEqual(process.state, ProcessStates.BACKOFF)
  861. self.assertEqual(L, [])
  862. def test_transition_starting_to_running(self):
  863. from supervisor import events
  864. L = []
  865. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  866. from supervisor.states import ProcessStates
  867. options = DummyOptions()
  868. # this should go from STARTING to RUNNING via transition()
  869. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  870. process = self._makeOne(pconfig)
  871. process.backoff = 1
  872. process.delay = 1
  873. process.system_stop = 0
  874. process.laststart = 1
  875. process.pid = 1
  876. process.stdout_buffer = 'abc'
  877. process.stderr_buffer = 'def'
  878. process.state = ProcessStates.STARTING
  879. process.transition()
  880. # this implies RUNNING
  881. self.assertEqual(process.backoff, 0)
  882. self.assertEqual(process.delay, 0)
  883. self.assertEqual(process.system_stop, 0)
  884. self.assertEqual(options.logger.data[0],
  885. 'success: process entered RUNNING state, process has '
  886. 'stayed up for > than 10 seconds (startsecs)')
  887. self.assertEqual(L[0].__class__, events.RunningFromStartingEvent)
  888. def test_transition_backoff_to_fatal(self):
  889. from supervisor import events
  890. L = []
  891. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  892. from supervisor.states import ProcessStates
  893. options = DummyOptions()
  894. # this should go from BACKOFF to FATAL via transition()
  895. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  896. process = self._makeOne(pconfig)
  897. process.laststart = 1
  898. process.backoff = 10000
  899. process.delay = 1
  900. process.system_stop = 0
  901. process.stdout_buffer = 'abc'
  902. process.stderr_buffer = 'def'
  903. process.state = ProcessStates.BACKOFF
  904. process.transition()
  905. # this implies FATAL
  906. self.assertEqual(process.backoff, 0)
  907. self.assertEqual(process.delay, 0)
  908. self.assertEqual(process.system_stop, 1)
  909. self.assertEqual(options.logger.data[0],
  910. 'gave up: process entered FATAL state, too many start'
  911. ' retries too quickly')
  912. self.assertEqual(L[0].__class__, events.FatalFromBackoffEvent)
  913. def test_transition_stops_unkillable_notyet(self):
  914. from supervisor import events
  915. L = []
  916. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  917. from supervisor.states import ProcessStates
  918. options = DummyOptions()
  919. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  920. process = self._makeOne(pconfig)
  921. process.delay = sys.maxint
  922. process.state = ProcessStates.STOPPING
  923. process.transition()
  924. self.assertEqual(process.state, ProcessStates.STOPPING)
  925. self.assertEqual(L, [])
  926. def test_transition_stops_unkillable(self):
  927. from supervisor import events
  928. L = []
  929. events.subscribe(events.ProcessStateChangeEvent, lambda x: L.append(x))
  930. from supervisor.states import ProcessStates
  931. options = DummyOptions()
  932. pconfig = DummyPConfig(options, 'process', 'process','/bin/process')
  933. process = self._makeOne(pconfig)
  934. process.delay = 0
  935. process.pid = 1
  936. process.killing = 0
  937. process.state = ProcessStates.STOPPING
  938. process.transition()
  939. self.assertEqual(process.killing, 1)
  940. self.assertNotEqual(process.delay, 0)
  941. self.assertEqual(process.state, ProcessStates.STOPPING)
  942. self.assertEqual(options.logger.data[0],
  943. "killing 'process' (1) with SIGKILL")
  944. import signal
  945. self.assertEqual(options.kills[1], signal.SIGKILL)
  946. self.assertEqual(L, [])
  947. def test_change_state_doesnt_notify_if_no_state_change(self):
  948. options = DummyOptions()
  949. config = DummyPConfig(options, 'test', '/test')
  950. instance = self._makeOne(config)
  951. instance.state = 10
  952. self.assertEqual(instance.change_state(10), False)
  953. class ProcessGroupBaseTests(unittest.TestCase):
  954. def _getTargetClass(self):
  955. from supervisor.process import ProcessGroupBase
  956. return ProcessGroupBase
  957. def _makeOne(self, *args, **kw):
  958. return self._getTargetClass()(*args, **kw)
  959. def test_get_delay_processes(self):
  960. options = DummyOptions()
  961. from supervisor.states import ProcessStates
  962. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  963. process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
  964. process1.delay = 1
  965. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  966. group = self._makeOne(gconfig)
  967. group.processes = { 'process1': process1 }
  968. delayed = group.get_delay_processes()
  969. self.assertEqual(delayed, [process1])
  970. def test_stop_all(self):
  971. from supervisor.states import ProcessStates
  972. options = DummyOptions()
  973. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  974. process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPED)
  975. pconfig2 = DummyPConfig(options, 'process2', 'process2','/bin/process2')
  976. process2 = DummyProcess(pconfig2, state=ProcessStates.RUNNING)
  977. pconfig3 = DummyPConfig(options, 'process3', 'process3','/bin/process3')
  978. process3 = DummyProcess(pconfig3, state=ProcessStates.STARTING)
  979. pconfig4 = DummyPConfig(options, 'process4', 'process4','/bin/process4')
  980. process4 = DummyProcess(pconfig4, state=ProcessStates.BACKOFF)
  981. process4.delay = 1000
  982. process4.backoff = 10
  983. gconfig = DummyPGroupConfig(
  984. options,
  985. pconfigs=[pconfig1, pconfig2, pconfig3, pconfig4])
  986. group = self._makeOne(gconfig)
  987. group.processes = {'process1': process1, 'process2': process2,
  988. 'process3':process3, 'process4':process4}
  989. group.stop_all()
  990. self.assertEqual(process1.stop_called, False)
  991. self.assertEqual(process2.stop_called, True)
  992. self.assertEqual(process3.stop_called, True)
  993. self.assertEqual(process4.stop_called, False)
  994. self.assertEqual(process4.state, ProcessStates.FATAL)
  995. def test_get_dispatchers(self):
  996. options = DummyOptions()
  997. from supervisor.states import ProcessStates
  998. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  999. process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
  1000. process1.dispatchers = {4:None}
  1001. pconfig2 = DummyPConfig(options, 'process2', 'process2','/bin/process2')
  1002. process2 = DummyProcess(pconfig2, state=ProcessStates.STOPPING)
  1003. process2.dispatchers = {5:None}
  1004. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1, pconfig2])
  1005. group = self._makeOne(gconfig)
  1006. group.processes = { 'process1': process1, 'process2': process2 }
  1007. result= group.get_dispatchers()
  1008. self.assertEqual(result, {4:None, 5:None})
  1009. def test_reopenlogs(self):
  1010. options = DummyOptions()
  1011. from supervisor.states import ProcessStates
  1012. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1013. process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
  1014. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1015. group = self._makeOne(gconfig)
  1016. group.processes = {'process1': process1}
  1017. group.reopenlogs()
  1018. self.assertEqual(process1.logs_reopened, True)
  1019. def test_removelogs(self):
  1020. options = DummyOptions()
  1021. from supervisor.states import ProcessStates
  1022. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1023. process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
  1024. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1025. group = self._makeOne(gconfig)
  1026. group.processes = {'process1': process1}
  1027. group.removelogs()
  1028. self.assertEqual(process1.logsremoved, True)
  1029. def test_cmp(self):
  1030. options = DummyOptions()
  1031. gconfig1 = DummyPGroupConfig(options)
  1032. group1 = self._makeOne(gconfig1)
  1033. gconfig2 = DummyPGroupConfig(options)
  1034. group2 = self._makeOne(gconfig2)
  1035. group1.priority = 5
  1036. group2.priority = 1
  1037. L = [group1, group2]
  1038. L.sort()
  1039. self.assertEqual(L, [group2, group1])
  1040. class ProcessGroupTests(ProcessGroupBaseTests):
  1041. def _getTargetClass(self):
  1042. from supervisor.process import ProcessGroup
  1043. return ProcessGroup
  1044. def test_repr(self):
  1045. options = DummyOptions()
  1046. gconfig = DummyPGroupConfig(options)
  1047. group = self._makeOne(gconfig)
  1048. s = repr(group)
  1049. self.assertTrue(s.startswith(
  1050. '<supervisor.process.ProcessGroup instance at'), s)
  1051. self.assertTrue(s.endswith('named whatever>'), s)
  1052. def test_transition(self):
  1053. options = DummyOptions()
  1054. from supervisor.states import ProcessStates
  1055. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1056. process1 = DummyProcess(pconfig1, state=ProcessStates.STOPPING)
  1057. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1058. group = self._makeOne(gconfig)
  1059. group.processes = {'process1': process1}
  1060. group.transition()
  1061. self.assertEqual(process1.transitioned, True)
  1062. class EventListenerPoolTests(ProcessGroupBaseTests):
  1063. def setUp(self):
  1064. from supervisor.events import clear
  1065. clear()
  1066. def tearDown(self):
  1067. from supervisor.events import clear
  1068. clear()
  1069. def _getTargetClass(self):
  1070. from supervisor.process import EventListenerPool
  1071. return EventListenerPool
  1072. def test_ctor(self):
  1073. options = DummyOptions()
  1074. gconfig = DummyPGroupConfig(options)
  1075. class EventType:
  1076. pass
  1077. gconfig.pool_events = (EventType,)
  1078. pool = self._makeOne(gconfig)
  1079. from supervisor import events
  1080. self.assertEqual(len(events.callbacks), 2)
  1081. self.assertEqual(events.callbacks[0],
  1082. (EventType, pool._acceptEvent))
  1083. self.assertEqual(events.callbacks[1],
  1084. (events.EventRejectedEvent, pool.handle_rejected))
  1085. self.assertEqual(pool.serial, -1)
  1086. def test__eventEnvelope(self):
  1087. options = DummyOptions()
  1088. options.identifier = 'thesupervisorname'
  1089. gconfig = DummyPGroupConfig(options)
  1090. gconfig.name = 'thepoolname'
  1091. pool = self._makeOne(gconfig)
  1092. from supervisor import events
  1093. result = pool._eventEnvelope(
  1094. events.EventTypes.PROCESS_COMMUNICATION_STDOUT, 80, 20, 'payload\n')
  1095. header, payload = result.split('\n', 1)
  1096. headers = header.split()
  1097. self.assertEqual(headers[0], 'ver:3.0')
  1098. self.assertEqual(headers[1], 'server:thesupervisorname')
  1099. self.assertEqual(headers[2], 'serial:80')
  1100. self.assertEqual(headers[3], 'pool:thepoolname')
  1101. self.assertEqual(headers[4], 'poolserial:20')
  1102. self.assertEqual(headers[5], 'eventname:PROCESS_COMMUNICATION_STDOUT')
  1103. self.assertEqual(headers[6], 'len:8')
  1104. self.assertEqual(payload, 'payload\n')
  1105. def test_handle_rejected_no_overflow(self):
  1106. options = DummyOptions()
  1107. gconfig = DummyPGroupConfig(options)
  1108. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1109. process1 = DummyProcess(pconfig1)
  1110. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1111. pool = self._makeOne(gconfig)
  1112. pool.processes = {'process1': process1}
  1113. pool.event_buffer = [None, None]
  1114. class DummyEvent1:
  1115. serial = 'abc'
  1116. class DummyEvent2:
  1117. process = process1
  1118. event = DummyEvent1()
  1119. dummyevent = DummyEvent2()
  1120. dummyevent.serial = 1
  1121. pool.handle_rejected(dummyevent)
  1122. self.assertEqual(pool.event_buffer, [dummyevent.event, None, None])
  1123. def test_handle_rejected_event_buffer_overflowed(self):
  1124. options = DummyOptions()
  1125. gconfig = DummyPGroupConfig(options)
  1126. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1127. process1 = DummyProcess(pconfig1)
  1128. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1129. gconfig.buffer_size = 3
  1130. pool = self._makeOne(gconfig)
  1131. pool.processes = {'process1': process1}
  1132. class DummyEvent:
  1133. def __init__(self, serial):
  1134. self.serial = serial
  1135. class DummyRejectedEvent:
  1136. def __init__(self, serial):
  1137. self.process = process1
  1138. self.event = DummyEvent(serial)
  1139. event_a = DummyEvent('a')
  1140. event_b = DummyEvent('b')
  1141. event_c = DummyEvent('c')
  1142. rej_event = DummyRejectedEvent('rejected')
  1143. pool.event_buffer = [event_a, event_b, event_c]
  1144. pool.handle_rejected(rej_event)
  1145. serials = [ x.serial for x in pool.event_buffer ]
  1146. # we popped a, and we inserted the rejected event into the 1st pos
  1147. self.assertEqual(serials, ['rejected', 'b', 'c'])
  1148. self.assertEqual(pool.config.options.logger.data[0],
  1149. 'pool whatever event buffer overflowed, discarding event a')
  1150. def test_dispatch_pipe_error(self):
  1151. options = DummyOptions()
  1152. gconfig = DummyPGroupConfig(options)
  1153. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1154. from supervisor.states import EventListenerStates
  1155. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1156. pool = self._makeOne(gconfig)
  1157. process1 = pool.processes['process1']
  1158. import errno
  1159. process1.write_error = errno.EPIPE
  1160. process1.listener_state = EventListenerStates.READY
  1161. from supervisor.events import StartingFromStoppedEvent
  1162. event = StartingFromStoppedEvent(process1, 1)
  1163. pool._acceptEvent(event)
  1164. pool.dispatch()
  1165. self.assertEqual(process1.listener_state, EventListenerStates.READY)
  1166. self.assertEqual(pool.event_buffer, [event])
  1167. self.assertEqual(options.logger.data[0],
  1168. 'rebuffering event 1 for pool whatever (bufsize 0)')
  1169. def test__acceptEvent_attaches_pool_serial_and_serial(self):
  1170. from supervisor.process import GlobalSerial
  1171. options = DummyOptions()
  1172. gconfig = DummyPGroupConfig(options)
  1173. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1174. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1175. pool = self._makeOne(gconfig)
  1176. process1 = pool.processes['process1']
  1177. from supervisor.states import EventListenerStates
  1178. process1.listener_state = EventListenerStates.READY
  1179. from supervisor.events import StartingFromStoppedEvent
  1180. event = StartingFromStoppedEvent(process1, 1)
  1181. pool._acceptEvent(event)
  1182. self.assertEqual(event.serial, GlobalSerial.serial)
  1183. self.assertEqual(event.pool_serials['whatever'], pool.serial)
  1184. def test_repr(self):
  1185. options = DummyOptions()
  1186. gconfig = DummyPGroupConfig(options)
  1187. pool = self._makeOne(gconfig)
  1188. s = repr(pool)
  1189. self.assertTrue(s.startswith(
  1190. '<supervisor.process.EventListenerPool instance at'))
  1191. self.assertTrue(s.endswith('named whatever>'))
  1192. def test_transition_nobody_ready(self):
  1193. options = DummyOptions()
  1194. from supervisor.states import ProcessStates
  1195. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1196. process1 = DummyProcess(pconfig1, state=ProcessStates.STARTING)
  1197. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1198. pool = self._makeOne(gconfig)
  1199. pool.processes = {'process1': process1}
  1200. from supervisor.events import StartingFromStoppedEvent
  1201. from supervisor.states import EventListenerStates
  1202. event = StartingFromStoppedEvent(process1, 1)
  1203. event.serial = 'a'
  1204. process1.listener_state = EventListenerStates.BUSY
  1205. pool._acceptEvent(event)
  1206. pool.transition()
  1207. self.assertEqual(process1.transitioned, True)
  1208. self.assertEqual(pool.event_buffer, [event])
  1209. data = pool.config.options.logger.data
  1210. def test_transition_event_proc_not_running(self):
  1211. options = DummyOptions()
  1212. from supervisor.states import ProcessStates
  1213. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1214. process1 = DummyProcess(pconfig1, state=ProcessStates.STARTING)
  1215. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1216. pool = self._makeOne(gconfig)
  1217. pool.processes = {'process1': process1}
  1218. from supervisor.events import StartingFromStoppedEvent
  1219. from supervisor.states import EventListenerStates
  1220. event = StartingFromStoppedEvent(process1, 1)
  1221. event.serial = 1
  1222. process1.listener_state = EventListenerStates.READY
  1223. pool._acceptEvent(event)
  1224. pool.transition()
  1225. self.assertEqual(process1.transitioned, True)
  1226. self.assertEqual(pool.event_buffer, [event])
  1227. self.assertEqual(process1.stdin_buffer, '')
  1228. self.assertEqual(process1.listener_state, EventListenerStates.READY)
  1229. def test_transition_event_proc_running(self):
  1230. options = DummyOptions()
  1231. from supervisor.states import ProcessStates
  1232. pconfig1 = DummyPConfig(options, 'process1', 'process1','/bin/process1')
  1233. process1 = DummyProcess(pconfig1, state=ProcessStates.RUNNING)
  1234. gconfig = DummyPGroupConfig(options, pconfigs=[pconfig1])
  1235. pool = self._makeOne(gconfig)
  1236. pool.processes = {'process1': process1}
  1237. from supervisor.events import StartingFromStoppedEvent
  1238. from supervisor.states import EventListenerStates
  1239. event = StartingFromStoppedEvent(process1, 1)
  1240. process1.listener_state = EventListenerStates.READY
  1241. class DummyGroup:
  1242. config = gconfig
  1243. process1.group = DummyGroup
  1244. pool._acceptEvent(event)
  1245. pool.transition()
  1246. self.assertEqual(process1.transitioned, True)
  1247. self.assertEqual(pool.event_buffer, [])
  1248. header, payload = process1.stdin_buffer.split('\n', 1)
  1249. self.assertEquals(payload,
  1250. 'processname:process1 groupname:whatever pid:1', payload)
  1251. headers = header.split()
  1252. self.assertEqual(
  1253. headers[5],
  1254. 'eventname:PROCESS_STATE_CHANGE_STARTING_FROM_STOPPED')
  1255. self.assertEqual(process1.listener_state, EventListenerStates.BUSY)
  1256. self.assertEqual(process1.event, event)
  1257. def test_suite():
  1258. return unittest.findTestCases(sys.modules[__name__])
  1259. if __name__ == '__main__':
  1260. unittest.main(defaultTest='test_suite')