test_options.py 65 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778
  1. """Test suite for supervisor.options"""
  2. import os
  3. import sys
  4. import tempfile
  5. import socket
  6. import unittest
  7. import signal
  8. import shutil
  9. import errno
  10. try:
  11. # Python < 3
  12. from StringIO import StringIO
  13. except ImportError:
  14. # Python >= 3
  15. from io import StringIO
  16. from mock import Mock, patch, sentinel
  17. from supervisor.tests.base import DummySupervisor
  18. from supervisor.tests.base import DummyLogger
  19. from supervisor.tests.base import DummyOptions
  20. from supervisor.tests.base import DummyPConfig
  21. from supervisor.tests.base import DummyProcess
  22. from supervisor.tests.base import DummySocketConfig
  23. from supervisor.tests.base import lstrip
  24. class OptionTests(unittest.TestCase):
  25. def _makeOptions(self, read_error=False):
  26. from cStringIO import StringIO
  27. from supervisor.options import Options
  28. from supervisor.datatypes import integer
  29. class MyOptions(Options):
  30. master = {
  31. 'other': 41 }
  32. def __init__(self, read_error=read_error):
  33. self.read_error = read_error
  34. Options.__init__(self)
  35. class Foo(object): pass
  36. self.configroot = Foo()
  37. def read_config(self, fp):
  38. if self.read_error:
  39. raise ValueError(self.read_error)
  40. # Pretend we read it from file:
  41. self.configroot.__dict__.update(self.default_map)
  42. self.configroot.__dict__.update(self.master)
  43. options = MyOptions()
  44. options.configfile = StringIO()
  45. options.add(name='anoption', confname='anoption',
  46. short='o', long='option', default='default')
  47. options.add(name='other', confname='other', env='OTHER',
  48. short='p:', long='other=', handler=integer)
  49. return options
  50. def test_searchpaths(self):
  51. options = self._makeOptions()
  52. self.assertEquals(len(options.searchpaths), 5)
  53. self.assertTrue('supervisord.conf' in options.searchpaths)
  54. self.assertTrue('etc/supervisord.conf' in options.searchpaths)
  55. self.assertTrue('/etc/supervisord.conf' in options.searchpaths)
  56. def test_options_and_args_order(self):
  57. # Only config file exists
  58. options = self._makeOptions()
  59. options.realize([])
  60. self.assertEquals(options.anoption, 'default')
  61. self.assertEquals(options.other, 41)
  62. # Env should trump config
  63. options = self._makeOptions()
  64. os.environ['OTHER'] = '42'
  65. options.realize([])
  66. self.assertEquals(options.other, 42)
  67. # Opt should trump both env (still set) and config
  68. options = self._makeOptions()
  69. options.realize(['-p', '43'])
  70. self.assertEquals(options.other, 43)
  71. del os.environ['OTHER']
  72. def test_config_reload(self):
  73. options = self._makeOptions()
  74. options.realize([])
  75. self.assertEquals(options.other, 41)
  76. options.master['other'] = 42
  77. options.process_config()
  78. self.assertEquals(options.other, 42)
  79. def test_config_reload_do_usage_false(self):
  80. options = self._makeOptions(read_error='error')
  81. self.assertRaises(ValueError, options.process_config,
  82. False)
  83. def test_config_reload_do_usage_true(self):
  84. options = self._makeOptions(read_error='error')
  85. from StringIO import StringIO
  86. L = []
  87. def exit(num):
  88. L.append(num)
  89. options.stderr = options.stdout = StringIO()
  90. options.exit = exit
  91. options.configroot.anoption = 1
  92. options.configroot.other = 1
  93. options.process_config(True)
  94. self.assertEqual(L, [2])
  95. def test__set(self):
  96. from supervisor.options import Options
  97. options = Options()
  98. options._set('foo', 'bar', 0)
  99. self.assertEquals(options.foo, 'bar')
  100. self.assertEquals(options.attr_priorities['foo'], 0)
  101. options._set('foo', 'baz', 1)
  102. self.assertEquals(options.foo, 'baz')
  103. self.assertEquals(options.attr_priorities['foo'], 1)
  104. options._set('foo', 'gazonk', 0)
  105. self.assertEquals(options.foo, 'baz')
  106. self.assertEquals(options.attr_priorities['foo'], 1)
  107. options._set('foo', 'gazonk', 1)
  108. self.assertEquals(options.foo, 'gazonk')
  109. class ClientOptionsTests(unittest.TestCase):
  110. def _getTargetClass(self):
  111. from supervisor.options import ClientOptions
  112. return ClientOptions
  113. def _makeOne(self):
  114. return self._getTargetClass()()
  115. def test_no_config_file(self):
  116. """Making sure config file is not required."""
  117. instance = self._makeOne()
  118. # No default config file search in case they would exist
  119. self.assertTrue(len(instance.searchpaths) > 0)
  120. instance.searchpaths = []
  121. class DummyException(Exception):
  122. pass
  123. def dummy_exit(self, _exitcode=0):
  124. raise DummyException()
  125. instance.exit = dummy_exit
  126. instance.realize(args=['-s', 'http://localhost:9001', '-u', 'chris',
  127. '-p', '123'])
  128. self.assertEquals(instance.interactive, 1)
  129. self.assertEqual(instance.serverurl, 'http://localhost:9001')
  130. self.assertEqual(instance.username, 'chris')
  131. self.assertEqual(instance.password, '123')
  132. def test_options(self):
  133. tempdir = tempfile.gettempdir()
  134. s = lstrip("""[supervisorctl]
  135. serverurl=http://localhost:9001
  136. username=chris
  137. password=123
  138. prompt=mysupervisor
  139. history_file=%s/sc_history
  140. """ % tempdir)
  141. from StringIO import StringIO
  142. fp = StringIO(s)
  143. instance = self._makeOne()
  144. instance.configfile = fp
  145. instance.realize(args=[])
  146. self.assertEqual(instance.interactive, True)
  147. history_file = os.path.join(tempdir, 'sc_history')
  148. self.assertEqual(instance.history_file, history_file)
  149. options = instance.configroot.supervisorctl
  150. self.assertEqual(options.prompt, 'mysupervisor')
  151. self.assertEqual(options.serverurl, 'http://localhost:9001')
  152. self.assertEqual(options.username, 'chris')
  153. self.assertEqual(options.password, '123')
  154. self.assertEqual(options.history_file, history_file)
  155. def test_unreadable_config_file(self):
  156. # Quick and dirty way of coming up with a decent filename
  157. tempf = tempfile.NamedTemporaryFile()
  158. fname = tempf.name
  159. tempf.close()
  160. self.assertFalse(os.path.exists(fname))
  161. instance = self._makeOne()
  162. class DummyException(Exception):
  163. def __init__(self, exitcode):
  164. self.exitcode = exitcode
  165. def dummy_exit(self, exitcode=2):
  166. # Important default exitcode=2 like sys.exit.
  167. raise DummyException(exitcode)
  168. instance.exit = dummy_exit
  169. try:
  170. instance.realize(args=['-c', fname])
  171. except DummyException, e:
  172. self.assertEquals(e.exitcode, 2)
  173. else:
  174. self.fail("expected exception")
  175. try:
  176. instance.read_config(fname)
  177. except ValueError, e:
  178. self.assertTrue("could not find config file" in str(e))
  179. else:
  180. self.fail("expected exception")
  181. tempf = tempfile.NamedTemporaryFile()
  182. os.chmod(tempf.name, 0) # Removing read perms
  183. try:
  184. instance.read_config(tempf.name)
  185. except ValueError, e:
  186. self.assertTrue("could not read config file" in str(e))
  187. else:
  188. self.fail("expected exception")
  189. tempf.close()
  190. def test_options_unixsocket_cli(self):
  191. from StringIO import StringIO
  192. fp = StringIO('[supervisorctl]')
  193. instance = self._makeOne()
  194. instance.configfile = fp
  195. instance.realize(args=['--serverurl', 'unix:///dev/null'])
  196. self.assertEqual(instance.serverurl, 'unix:///dev/null')
  197. class ServerOptionsTests(unittest.TestCase):
  198. def _getTargetClass(self):
  199. from supervisor.options import ServerOptions
  200. return ServerOptions
  201. def _makeOne(self):
  202. return self._getTargetClass()()
  203. def test_version(self):
  204. from supervisor.options import VERSION
  205. options = self._makeOne()
  206. from StringIO import StringIO
  207. options.stdout = StringIO()
  208. self.assertRaises(SystemExit, options.version, None)
  209. self.assertEqual(options.stdout.getvalue(), VERSION + '\n')
  210. def test_options(self):
  211. s = lstrip("""[inet_http_server]
  212. port=127.0.0.1:8999
  213. username=chrism
  214. password=foo
  215. [supervisord]
  216. directory=%(tempdir)s
  217. backofflimit=10
  218. user=root
  219. umask=022
  220. logfile=supervisord.log
  221. logfile_maxbytes=1000MB
  222. logfile_backups=5
  223. loglevel=error
  224. pidfile=supervisord.pid
  225. nodaemon=true
  226. identifier=fleeb
  227. childlogdir=%(tempdir)s
  228. nocleanup=true
  229. minfds=2048
  230. minprocs=300
  231. environment=FAKE_ENV_VAR=/some/path
  232. [program:cat1]
  233. command=/bin/cat
  234. priority=1
  235. autostart=true
  236. user=root
  237. stdout_logfile=/tmp/cat.log
  238. stopsignal=KILL
  239. stopwaitsecs=5
  240. startsecs=5
  241. startretries=10
  242. directory=/tmp
  243. umask=002
  244. [program:cat2]
  245. priority=2
  246. command=/bin/cat
  247. autostart=true
  248. autorestart=false
  249. stdout_logfile_maxbytes = 1024
  250. stdout_logfile_backups = 2
  251. stdout_logfile = /tmp/cat2.log
  252. [program:cat3]
  253. priority=3
  254. process_name = replaced
  255. command=/bin/cat
  256. autorestart=true
  257. exitcodes=0,1,127
  258. stopasgroup=true
  259. killasgroup=true
  260. [program:cat4]
  261. priority=4
  262. process_name = fleeb_%%(process_num)s
  263. numprocs = 2
  264. command = /bin/cat
  265. autorestart=unexpected
  266. """ % {'tempdir':tempfile.gettempdir()})
  267. from supervisor import datatypes
  268. from StringIO import StringIO
  269. fp = StringIO(s)
  270. instance = self._makeOne()
  271. instance.configfile = fp
  272. instance.realize(args=[])
  273. options = instance.configroot.supervisord
  274. self.assertEqual(options.directory, tempfile.gettempdir())
  275. self.assertEqual(options.umask, 022)
  276. self.assertEqual(options.logfile, 'supervisord.log')
  277. self.assertEqual(options.logfile_maxbytes, 1000 * 1024 * 1024)
  278. self.assertEqual(options.logfile_backups, 5)
  279. self.assertEqual(options.loglevel, 40)
  280. self.assertEqual(options.pidfile, 'supervisord.pid')
  281. self.assertEqual(options.nodaemon, True)
  282. self.assertEqual(options.identifier, 'fleeb')
  283. self.assertEqual(options.childlogdir, tempfile.gettempdir())
  284. self.assertEqual(len(options.server_configs), 1)
  285. self.assertEqual(options.server_configs[0]['family'], socket.AF_INET)
  286. self.assertEqual(options.server_configs[0]['host'], '127.0.0.1')
  287. self.assertEqual(options.server_configs[0]['port'], 8999)
  288. self.assertEqual(options.server_configs[0]['username'], 'chrism')
  289. self.assertEqual(options.server_configs[0]['password'], 'foo')
  290. self.assertEqual(options.nocleanup, True)
  291. self.assertEqual(options.minfds, 2048)
  292. self.assertEqual(options.minprocs, 300)
  293. self.assertEqual(options.nocleanup, True)
  294. self.assertEqual(len(options.process_group_configs), 4)
  295. self.assertEqual(options.environment, dict(FAKE_ENV_VAR='/some/path'))
  296. cat1 = options.process_group_configs[0]
  297. self.assertEqual(cat1.name, 'cat1')
  298. self.assertEqual(cat1.priority, 1)
  299. self.assertEqual(len(cat1.process_configs), 1)
  300. proc1 = cat1.process_configs[0]
  301. self.assertEqual(proc1.name, 'cat1')
  302. self.assertEqual(proc1.command, '/bin/cat')
  303. self.assertEqual(proc1.priority, 1)
  304. self.assertEqual(proc1.autostart, True)
  305. self.assertEqual(proc1.autorestart, datatypes.RestartWhenExitUnexpected)
  306. self.assertEqual(proc1.startsecs, 5)
  307. self.assertEqual(proc1.startretries, 10)
  308. self.assertEqual(proc1.uid, 0)
  309. self.assertEqual(proc1.stdout_logfile, '/tmp/cat.log')
  310. self.assertEqual(proc1.stopsignal, signal.SIGKILL)
  311. self.assertEqual(proc1.stopwaitsecs, 5)
  312. self.assertEqual(proc1.stopasgroup, False)
  313. self.assertEqual(proc1.killasgroup, False)
  314. self.assertEqual(proc1.stdout_logfile_maxbytes,
  315. datatypes.byte_size('50MB'))
  316. self.assertEqual(proc1.stdout_logfile_backups, 10)
  317. self.assertEqual(proc1.exitcodes, [0,2])
  318. self.assertEqual(proc1.directory, '/tmp')
  319. self.assertEqual(proc1.umask, 002)
  320. self.assertEqual(proc1.environment, dict(FAKE_ENV_VAR='/some/path'))
  321. cat2 = options.process_group_configs[1]
  322. self.assertEqual(cat2.name, 'cat2')
  323. self.assertEqual(cat2.priority, 2)
  324. self.assertEqual(len(cat2.process_configs), 1)
  325. proc2 = cat2.process_configs[0]
  326. self.assertEqual(proc2.name, 'cat2')
  327. self.assertEqual(proc2.command, '/bin/cat')
  328. self.assertEqual(proc2.priority, 2)
  329. self.assertEqual(proc2.autostart, True)
  330. self.assertEqual(proc2.autorestart, False)
  331. self.assertEqual(proc2.uid, None)
  332. self.assertEqual(proc2.stdout_logfile, '/tmp/cat2.log')
  333. self.assertEqual(proc2.stopsignal, signal.SIGTERM)
  334. self.assertEqual(proc2.stopasgroup, False)
  335. self.assertEqual(proc2.killasgroup, False)
  336. self.assertEqual(proc2.stdout_logfile_maxbytes, 1024)
  337. self.assertEqual(proc2.stdout_logfile_backups, 2)
  338. self.assertEqual(proc2.exitcodes, [0,2])
  339. self.assertEqual(proc2.directory, None)
  340. cat3 = options.process_group_configs[2]
  341. self.assertEqual(cat3.name, 'cat3')
  342. self.assertEqual(cat3.priority, 3)
  343. self.assertEqual(len(cat3.process_configs), 1)
  344. proc3 = cat3.process_configs[0]
  345. self.assertEqual(proc3.name, 'replaced')
  346. self.assertEqual(proc3.command, '/bin/cat')
  347. self.assertEqual(proc3.priority, 3)
  348. self.assertEqual(proc3.autostart, True)
  349. self.assertEqual(proc3.autorestart, datatypes.RestartUnconditionally)
  350. self.assertEqual(proc3.uid, None)
  351. self.assertEqual(proc3.stdout_logfile, datatypes.Automatic)
  352. self.assertEqual(proc3.stdout_logfile_maxbytes,
  353. datatypes.byte_size('50MB'))
  354. self.assertEqual(proc3.stdout_logfile_backups, 10)
  355. self.assertEqual(proc3.exitcodes, [0,1,127])
  356. self.assertEqual(proc3.stopsignal, signal.SIGTERM)
  357. self.assertEqual(proc3.stopasgroup, True)
  358. self.assertEqual(proc3.killasgroup, True)
  359. cat4 = options.process_group_configs[3]
  360. self.assertEqual(cat4.name, 'cat4')
  361. self.assertEqual(cat4.priority, 4)
  362. self.assertEqual(len(cat4.process_configs), 2)
  363. proc4_a = cat4.process_configs[0]
  364. self.assertEqual(proc4_a.name, 'fleeb_0')
  365. self.assertEqual(proc4_a.command, '/bin/cat')
  366. self.assertEqual(proc4_a.priority, 4)
  367. self.assertEqual(proc4_a.autostart, True)
  368. self.assertEqual(proc4_a.autorestart,
  369. datatypes.RestartWhenExitUnexpected)
  370. self.assertEqual(proc4_a.uid, None)
  371. self.assertEqual(proc4_a.stdout_logfile, datatypes.Automatic)
  372. self.assertEqual(proc4_a.stdout_logfile_maxbytes,
  373. datatypes.byte_size('50MB'))
  374. self.assertEqual(proc4_a.stdout_logfile_backups, 10)
  375. self.assertEqual(proc4_a.exitcodes, [0,2])
  376. self.assertEqual(proc4_a.stopsignal, signal.SIGTERM)
  377. self.assertEqual(proc4_a.stopasgroup, False)
  378. self.assertEqual(proc4_a.killasgroup, False)
  379. proc4_b = cat4.process_configs[1]
  380. self.assertEqual(proc4_b.name, 'fleeb_1')
  381. self.assertEqual(proc4_b.command, '/bin/cat')
  382. self.assertEqual(proc4_b.priority, 4)
  383. self.assertEqual(proc4_b.autostart, True)
  384. self.assertEqual(proc4_b.autorestart,
  385. datatypes.RestartWhenExitUnexpected)
  386. self.assertEqual(proc4_b.uid, None)
  387. self.assertEqual(proc4_b.stdout_logfile, datatypes.Automatic)
  388. self.assertEqual(proc4_b.stdout_logfile_maxbytes,
  389. datatypes.byte_size('50MB'))
  390. self.assertEqual(proc4_b.stdout_logfile_backups, 10)
  391. self.assertEqual(proc4_b.exitcodes, [0,2])
  392. self.assertEqual(proc4_b.stopsignal, signal.SIGTERM)
  393. self.assertEqual(proc4_b.stopasgroup, False)
  394. self.assertEqual(proc4_b.killasgroup, False)
  395. here = os.path.abspath(os.getcwd())
  396. self.assertEqual(instance.uid, 0)
  397. self.assertEqual(instance.gid, 0)
  398. self.assertEqual(instance.directory, tempfile.gettempdir())
  399. self.assertEqual(instance.umask, 022)
  400. self.assertEqual(instance.logfile, os.path.join(here,'supervisord.log'))
  401. self.assertEqual(instance.logfile_maxbytes, 1000 * 1024 * 1024)
  402. self.assertEqual(instance.logfile_backups, 5)
  403. self.assertEqual(instance.loglevel, 40)
  404. self.assertEqual(instance.pidfile, os.path.join(here,'supervisord.pid'))
  405. self.assertEqual(instance.nodaemon, True)
  406. self.assertEqual(instance.passwdfile, None)
  407. self.assertEqual(instance.identifier, 'fleeb')
  408. self.assertEqual(instance.childlogdir, tempfile.gettempdir())
  409. self.assertEqual(len(instance.server_configs), 1)
  410. self.assertEqual(instance.server_configs[0]['family'], socket.AF_INET)
  411. self.assertEqual(instance.server_configs[0]['host'], '127.0.0.1')
  412. self.assertEqual(instance.server_configs[0]['port'], 8999)
  413. self.assertEqual(instance.server_configs[0]['username'], 'chrism')
  414. self.assertEqual(instance.server_configs[0]['password'], 'foo')
  415. self.assertEqual(instance.nocleanup, True)
  416. self.assertEqual(instance.minfds, 2048)
  417. self.assertEqual(instance.minprocs, 300)
  418. def test_no_config_file_exits(self):
  419. instance = self._makeOne()
  420. # No default config file search in case they would exist
  421. self.assertTrue(len(instance.searchpaths) > 0)
  422. instance.searchpaths = []
  423. class DummyException(Exception):
  424. def __init__(self, exitcode):
  425. self.exitcode = exitcode
  426. def dummy_exit(exitcode=2):
  427. # Important default exitcode=2 like sys.exit.
  428. raise DummyException(exitcode)
  429. instance.exit = dummy_exit
  430. # Making sure we capture stdout and stderr
  431. instance.stderr = StringIO()
  432. try:
  433. instance.realize()
  434. except DummyException, e:
  435. # Caught expected exception
  436. import traceback
  437. self.assertEquals(e.exitcode, 2,
  438. "Wrong exitcode for: %s" % traceback.format_exc(e))
  439. else:
  440. self.fail("Did not get a DummyException.")
  441. def test_reload(self):
  442. from cStringIO import StringIO
  443. text = lstrip("""\
  444. [supervisord]
  445. user=root
  446. [program:one]
  447. command = /bin/cat
  448. [program:two]
  449. command = /bin/dog
  450. [program:four]
  451. command = /bin/sheep
  452. [group:thegroup]
  453. programs = one,two
  454. """)
  455. instance = self._makeOne()
  456. instance.configfile = StringIO(text)
  457. instance.realize(args=[])
  458. section = instance.configroot.supervisord
  459. self.assertEqual(len(section.process_group_configs), 2)
  460. cat = section.process_group_configs[0]
  461. self.assertEqual(len(cat.process_configs), 1)
  462. cat = section.process_group_configs[1]
  463. self.assertEqual(len(cat.process_configs), 2)
  464. self.assertTrue(section.process_group_configs is
  465. instance.process_group_configs)
  466. text = lstrip("""\
  467. [supervisord]
  468. user=root
  469. [program:one]
  470. command = /bin/cat
  471. [program:three]
  472. command = /bin/pig
  473. [group:thegroup]
  474. programs = three
  475. """)
  476. instance.configfile = StringIO(text)
  477. instance.process_config()
  478. section = instance.configroot.supervisord
  479. self.assertEqual(len(section.process_group_configs), 2)
  480. cat = section.process_group_configs[0]
  481. self.assertEqual(len(cat.process_configs), 1)
  482. proc = cat.process_configs[0]
  483. self.assertEqual(proc.name, 'one')
  484. self.assertEqual(proc.command, '/bin/cat')
  485. self.assertTrue(section.process_group_configs is
  486. instance.process_group_configs)
  487. cat = section.process_group_configs[1]
  488. self.assertEqual(len(cat.process_configs), 1)
  489. proc = cat.process_configs[0]
  490. self.assertEqual(proc.name, 'three')
  491. self.assertEqual(proc.command, '/bin/pig')
  492. def test_reload_clears_parse_warnings(self):
  493. instance = self._makeOne()
  494. old_warning = "Warning from a prior config read"
  495. instance.parse_warnings = [old_warning]
  496. from cStringIO import StringIO
  497. text = lstrip("""\
  498. [supervisord]
  499. user=root
  500. [program:cat]
  501. command = /bin/cat
  502. """)
  503. instance.configfile = StringIO(text)
  504. instance.realize(args=[])
  505. self.assertFalse(old_warning in instance.parse_warnings)
  506. def test_unreadable_config_file(self):
  507. # Quick and dirty way of coming up with a decent filename
  508. tempf = tempfile.NamedTemporaryFile()
  509. fname = tempf.name
  510. tempf.close()
  511. self.assertFalse(os.path.exists(fname))
  512. instance = self._makeOne()
  513. class DummyException(Exception):
  514. def __init__(self, exitcode):
  515. self.exitcode = exitcode
  516. def dummy_exit(self, exitcode=2):
  517. # Important default exitcode=2 like sys.exit.
  518. raise DummyException(exitcode)
  519. instance.exit = dummy_exit
  520. try:
  521. instance.realize(args=['-c', fname])
  522. except DummyException, e:
  523. self.assertEquals(e.exitcode, 2)
  524. else:
  525. self.fail("expected exception")
  526. try:
  527. instance.read_config(fname)
  528. except ValueError, e:
  529. self.assertTrue("could not find config file" in str(e))
  530. else:
  531. self.fail("expected exception")
  532. tempf = tempfile.NamedTemporaryFile()
  533. os.chmod(tempf.name, 0) # Removing read perms
  534. try:
  535. instance.read_config(tempf.name)
  536. except ValueError, e:
  537. self.assertTrue("could not read config file" in str(e))
  538. else:
  539. self.fail("expected exception")
  540. tempf.close()
  541. def test_readFile_failed(self):
  542. from supervisor.options import readFile
  543. try:
  544. readFile('/notthere', 0, 10)
  545. except ValueError, inst:
  546. self.assertEqual(inst.args[0], 'FAILED')
  547. else:
  548. raise AssertionError("Didn't raise")
  549. def test_get_pid(self):
  550. instance = self._makeOne()
  551. self.assertEqual(os.getpid(), instance.get_pid())
  552. def test_get_signal_delegates_to_signal_receiver(self):
  553. instance = self._makeOne()
  554. instance.signal_receiver.receive(signal.SIGTERM, None)
  555. instance.signal_receiver.receive(signal.SIGCHLD, None)
  556. self.assertEqual(instance.get_signal(), signal.SIGTERM)
  557. self.assertEqual(instance.get_signal(), signal.SIGCHLD)
  558. self.assertEqual(instance.get_signal(), None)
  559. def test_check_execv_args_cant_find_command(self):
  560. instance = self._makeOne()
  561. from supervisor.options import NotFound
  562. self.assertRaises(NotFound, instance.check_execv_args,
  563. '/not/there', None, None)
  564. def test_check_execv_args_notexecutable(self):
  565. instance = self._makeOne()
  566. from supervisor.options import NotExecutable
  567. self.assertRaises(NotExecutable,
  568. instance.check_execv_args, '/etc/passwd',
  569. ['etc/passwd'], os.stat('/etc/passwd'))
  570. def test_check_execv_args_isdir(self):
  571. instance = self._makeOne()
  572. from supervisor.options import NotExecutable
  573. self.assertRaises(NotExecutable,
  574. instance.check_execv_args, '/',
  575. ['/'], os.stat('/'))
  576. def test_cleanup_afunix_unlink(self):
  577. fn = tempfile.mktemp()
  578. f = open(fn, 'w')
  579. f.write('foo')
  580. f.close()
  581. instance = self._makeOne()
  582. class Port:
  583. family = socket.AF_UNIX
  584. address = fn
  585. class Server:
  586. pass
  587. instance.httpservers = [({'family':socket.AF_UNIX, 'file':fn},
  588. Server())]
  589. instance.pidfile = ''
  590. instance.cleanup()
  591. self.failIf(os.path.exists(fn))
  592. def test_cleanup_afunix_nounlink(self):
  593. fn = tempfile.mktemp()
  594. try:
  595. f = open(fn, 'w')
  596. f.write('foo')
  597. f.close()
  598. instance = self._makeOne()
  599. class Port:
  600. family = socket.AF_UNIX
  601. address = fn
  602. class Server:
  603. pass
  604. instance.httpservers = [({'family':socket.AF_UNIX, 'file':fn},
  605. Server())]
  606. instance.pidfile = ''
  607. instance.unlink_socketfiles = False
  608. instance.cleanup()
  609. self.failUnless(os.path.exists(fn))
  610. finally:
  611. try:
  612. os.unlink(fn)
  613. except OSError:
  614. pass
  615. def test_close_httpservers(self):
  616. instance = self._makeOne()
  617. class Server:
  618. closed = False
  619. def close(self):
  620. self.closed = True
  621. server = Server()
  622. instance.httpservers = [({}, server)]
  623. instance.close_httpservers()
  624. self.assertEqual(server.closed, True)
  625. def test_close_logger(self):
  626. instance = self._makeOne()
  627. logger = DummyLogger()
  628. instance.logger = logger
  629. instance.close_logger()
  630. self.assertEqual(logger.closed, True)
  631. def test_write_pidfile_ok(self):
  632. fn = tempfile.mktemp()
  633. try:
  634. instance = self._makeOne()
  635. instance.logger = DummyLogger()
  636. instance.pidfile = fn
  637. instance.write_pidfile()
  638. self.failUnless(os.path.exists(fn))
  639. pid = int(open(fn, 'r').read()[:-1])
  640. self.assertEqual(pid, os.getpid())
  641. msg = instance.logger.data[0]
  642. self.failUnless(msg.startswith('supervisord started with pid'))
  643. finally:
  644. try:
  645. os.unlink(fn)
  646. except OSError:
  647. pass
  648. def test_write_pidfile_fail(self):
  649. fn = '/cannot/possibly/exist'
  650. instance = self._makeOne()
  651. instance.logger = DummyLogger()
  652. instance.pidfile = fn
  653. instance.write_pidfile()
  654. msg = instance.logger.data[0]
  655. self.failUnless(msg.startswith('could not write pidfile'))
  656. def test_close_fd(self):
  657. instance = self._makeOne()
  658. innie, outie = os.pipe()
  659. os.read(innie, 0) # we can read it while its open
  660. os.write(outie, 'foo') # we can write to it while its open
  661. instance.close_fd(innie)
  662. self.assertRaises(OSError, os.read, innie, 0)
  663. instance.close_fd(outie)
  664. self.assertRaises(OSError, os.write, outie, 'foo')
  665. def test_processes_from_section(self):
  666. instance = self._makeOne()
  667. text = lstrip("""\
  668. [program:foo]
  669. command = /bin/cat
  670. priority = 1
  671. autostart = false
  672. autorestart = false
  673. startsecs = 100
  674. startretries = 100
  675. user = root
  676. stdout_logfile = NONE
  677. stdout_logfile_backups = 1
  678. stdout_logfile_maxbytes = 100MB
  679. stdout_events_enabled = true
  680. stopsignal = KILL
  681. stopwaitsecs = 100
  682. killasgroup = true
  683. exitcodes = 1,4
  684. redirect_stderr = false
  685. environment = KEY1=val1,KEY2=val2,KEY3=%(process_num)s
  686. numprocs = 2
  687. process_name = %(group_name)s_%(program_name)s_%(process_num)02d
  688. """)
  689. from supervisor.options import UnhosedConfigParser
  690. config = UnhosedConfigParser()
  691. config.read_string(text)
  692. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  693. self.assertEqual(len(pconfigs), 2)
  694. pconfig = pconfigs[0]
  695. self.assertEqual(pconfig.name, 'bar_foo_00')
  696. self.assertEqual(pconfig.command, '/bin/cat')
  697. self.assertEqual(pconfig.autostart, False)
  698. self.assertEqual(pconfig.autorestart, False)
  699. self.assertEqual(pconfig.startsecs, 100)
  700. self.assertEqual(pconfig.startretries, 100)
  701. self.assertEqual(pconfig.uid, 0)
  702. self.assertEqual(pconfig.stdout_logfile, None)
  703. self.assertEqual(pconfig.stdout_capture_maxbytes, 0)
  704. self.assertEqual(pconfig.stdout_logfile_maxbytes, 104857600)
  705. self.assertEqual(pconfig.stdout_events_enabled, True)
  706. self.assertEqual(pconfig.stopsignal, signal.SIGKILL)
  707. self.assertEqual(pconfig.stopasgroup, False)
  708. self.assertEqual(pconfig.killasgroup, True)
  709. self.assertEqual(pconfig.stopwaitsecs, 100)
  710. self.assertEqual(pconfig.exitcodes, [1,4])
  711. self.assertEqual(pconfig.redirect_stderr, False)
  712. self.assertEqual(pconfig.environment,
  713. {'KEY1':'val1', 'KEY2':'val2', 'KEY3':'0'})
  714. def test_processes_from_section_host_node_name_expansion(self):
  715. instance = self._makeOne()
  716. text = lstrip("""\
  717. [program:foo]
  718. command = /bin/foo --host=%(host_node_name)s
  719. """)
  720. from supervisor.options import UnhosedConfigParser
  721. config = UnhosedConfigParser()
  722. config.read_string(text)
  723. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  724. import platform
  725. expected = "/bin/foo --host=" + platform.node()
  726. self.assertEqual(pconfigs[0].command, expected)
  727. def test_processes_from_section_environment_variables_expansion(self):
  728. instance = self._makeOne()
  729. text = lstrip("""\
  730. [program:foo]
  731. command = /bin/foo --path='%(ENV_PATH)s'
  732. """)
  733. from supervisor.options import UnhosedConfigParser
  734. config = UnhosedConfigParser()
  735. config.read_string(text)
  736. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  737. expected = "/bin/foo --path='%s'" % os.environ['PATH']
  738. self.assertEqual(pconfigs[0].command, expected)
  739. def test_processes_from_section_no_procnum_in_processname(self):
  740. instance = self._makeOne()
  741. text = lstrip("""\
  742. [program:foo]
  743. command = /bin/cat
  744. numprocs = 2
  745. """)
  746. from supervisor.options import UnhosedConfigParser
  747. config = UnhosedConfigParser()
  748. config.read_string(text)
  749. self.assertRaises(ValueError, instance.processes_from_section,
  750. config, 'program:foo', None)
  751. def test_processes_from_section_no_command(self):
  752. instance = self._makeOne()
  753. text = lstrip("""\
  754. [program:foo]
  755. numprocs = 2
  756. """)
  757. from supervisor.options import UnhosedConfigParser
  758. config = UnhosedConfigParser()
  759. config.read_string(text)
  760. self.assertRaises(ValueError, instance.processes_from_section,
  761. config, 'program:foo', None)
  762. def test_processes_from_section_missing_replacement_in_process_name(self):
  763. instance = self._makeOne()
  764. text = lstrip("""\
  765. [program:foo]
  766. command = /bin/cat
  767. process_name = %(not_there)s
  768. """)
  769. from supervisor.options import UnhosedConfigParser
  770. config = UnhosedConfigParser()
  771. config.read_string(text)
  772. self.assertRaises(ValueError, instance.processes_from_section,
  773. config, 'program:foo', None)
  774. def test_processes_from_section_bad_expression_in_process_name(self):
  775. instance = self._makeOne()
  776. text = lstrip("""\
  777. [program:foo]
  778. command = /bin/cat
  779. process_name = %(program_name)
  780. """)
  781. from supervisor.options import UnhosedConfigParser
  782. config = UnhosedConfigParser()
  783. config.read_string(text)
  784. self.assertRaises(ValueError, instance.processes_from_section,
  785. config, 'program:foo', None)
  786. def test_processes_from_section_stopasgroup_implies_killasgroup(self):
  787. instance = self._makeOne()
  788. text = lstrip("""\
  789. [program:foo]
  790. command = /bin/cat
  791. process_name = %(program_name)s
  792. stopasgroup = true
  793. """)
  794. from supervisor.options import UnhosedConfigParser
  795. config = UnhosedConfigParser()
  796. config.read_string(text)
  797. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  798. self.assertEqual(len(pconfigs), 1)
  799. pconfig = pconfigs[0]
  800. self.assertEqual(pconfig.stopasgroup, True)
  801. self.assertEqual(pconfig.killasgroup, True)
  802. def test_processes_from_section_killasgroup_mismatch_w_stopasgroup(self):
  803. instance = self._makeOne()
  804. text = lstrip("""\
  805. [program:foo]
  806. command = /bin/cat
  807. process_name = %(program_name)s
  808. stopasgroup = true
  809. killasgroup = false
  810. """)
  811. from supervisor.options import UnhosedConfigParser
  812. config = UnhosedConfigParser()
  813. config.read_string(text)
  814. self.assertRaises(ValueError, instance.processes_from_section,
  815. config, 'program:foo', None)
  816. def test_processes_from_autolog_without_rollover(self):
  817. instance = self._makeOne()
  818. text = lstrip("""\
  819. [program:foo]
  820. command = /bin/foo
  821. stdout_logfile = AUTO
  822. stdout_logfile_maxbytes = 0
  823. stderr_logfile = AUTO
  824. stderr_logfile_maxbytes = 0
  825. """)
  826. from supervisor.options import UnhosedConfigParser
  827. config = UnhosedConfigParser()
  828. instance.logger = DummyLogger()
  829. config.read_string(text)
  830. instance.processes_from_section(config, 'program:foo', None)
  831. self.assertEqual(instance.parse_warnings[0],
  832. 'For [program:foo], AUTO logging used for stdout_logfile '
  833. 'without rollover, set maxbytes > 0 to avoid filling up '
  834. 'filesystem unintentionally')
  835. self.assertEqual(instance.parse_warnings[1],
  836. 'For [program:foo], AUTO logging used for stderr_logfile '
  837. 'without rollover, set maxbytes > 0 to avoid filling up '
  838. 'filesystem unintentionally')
  839. def test_homogeneous_process_groups_from_parser(self):
  840. text = lstrip("""\
  841. [program:many]
  842. process_name = %(program_name)s_%(process_num)s
  843. command = /bin/cat
  844. numprocs = 2
  845. priority = 1
  846. """)
  847. from supervisor.options import UnhosedConfigParser
  848. config = UnhosedConfigParser()
  849. config.read_string(text)
  850. instance = self._makeOne()
  851. gconfigs = instance.process_groups_from_parser(config)
  852. self.assertEqual(len(gconfigs), 1)
  853. gconfig = gconfigs[0]
  854. self.assertEqual(gconfig.name, 'many')
  855. self.assertEqual(gconfig.priority, 1)
  856. self.assertEqual(len(gconfig.process_configs), 2)
  857. def test_event_listener_pools_from_parser(self):
  858. text = lstrip("""\
  859. [eventlistener:dog]
  860. events=PROCESS_COMMUNICATION
  861. process_name = %(program_name)s_%(process_num)s
  862. command = /bin/dog
  863. numprocs = 2
  864. priority = 1
  865. [eventlistener:cat]
  866. events=PROCESS_COMMUNICATION
  867. process_name = %(program_name)s_%(process_num)s
  868. command = /bin/cat
  869. numprocs = 3
  870. [eventlistener:biz]
  871. events=PROCESS_COMMUNICATION
  872. process_name = %(program_name)s_%(process_num)s
  873. command = /bin/biz
  874. numprocs = 2
  875. """)
  876. from supervisor.options import UnhosedConfigParser
  877. from supervisor.dispatchers import default_handler
  878. config = UnhosedConfigParser()
  879. config.read_string(text)
  880. instance = self._makeOne()
  881. gconfigs = instance.process_groups_from_parser(config)
  882. self.assertEqual(len(gconfigs), 3)
  883. gconfig1 = gconfigs[0]
  884. self.assertEqual(gconfig1.name, 'biz')
  885. self.assertEqual(gconfig1.result_handler, default_handler)
  886. self.assertEqual(len(gconfig1.process_configs), 2)
  887. gconfig1 = gconfigs[1]
  888. self.assertEqual(gconfig1.name, 'cat')
  889. self.assertEqual(gconfig1.priority, -1)
  890. self.assertEqual(gconfig1.result_handler, default_handler)
  891. self.assertEqual(len(gconfig1.process_configs), 3)
  892. gconfig1 = gconfigs[2]
  893. self.assertEqual(gconfig1.name, 'dog')
  894. self.assertEqual(gconfig1.priority, 1)
  895. self.assertEqual(gconfig1.result_handler, default_handler)
  896. self.assertEqual(len(gconfig1.process_configs), 2)
  897. def test_event_listener_pool_with_event_results_handler(self):
  898. text = lstrip("""\
  899. [eventlistener:dog]
  900. events=PROCESS_COMMUNICATION
  901. command = /bin/dog
  902. result_handler = supervisor.tests.base:dummy_handler
  903. """)
  904. from supervisor.options import UnhosedConfigParser
  905. from supervisor.tests.base import dummy_handler
  906. config = UnhosedConfigParser()
  907. config.read_string(text)
  908. instance = self._makeOne()
  909. gconfigs = instance.process_groups_from_parser(config)
  910. self.assertEqual(len(gconfigs), 1)
  911. gconfig1 = gconfigs[0]
  912. self.assertEqual(gconfig1.result_handler, dummy_handler)
  913. def test_event_listener_pool_noeventsline(self):
  914. text = lstrip("""\
  915. [eventlistener:dog]
  916. process_name = %(program_name)s_%(process_num)s
  917. command = /bin/dog
  918. numprocs = 2
  919. priority = 1
  920. """)
  921. from supervisor.options import UnhosedConfigParser
  922. config = UnhosedConfigParser()
  923. config.read_string(text)
  924. instance = self._makeOne()
  925. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  926. def test_event_listener_pool_unknown_eventtype(self):
  927. text = lstrip("""\
  928. [eventlistener:dog]
  929. events=PROCESS_COMMUNICATION,THIS_EVENT_TYPE_DOESNT_EXIST
  930. process_name = %(program_name)s_%(process_num)s
  931. command = /bin/dog
  932. numprocs = 2
  933. priority = 1
  934. """)
  935. from supervisor.options import UnhosedConfigParser
  936. config = UnhosedConfigParser()
  937. config.read_string(text)
  938. instance = self._makeOne()
  939. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  940. def test_fcgi_programs_from_parser(self):
  941. from supervisor.options import FastCGIGroupConfig
  942. from supervisor.options import FastCGIProcessConfig
  943. text = lstrip("""\
  944. [fcgi-program:foo]
  945. socket = unix:///tmp/%(program_name)s.sock
  946. socket_owner = testuser:testgroup
  947. socket_mode = 0666
  948. process_name = %(program_name)s_%(process_num)s
  949. command = /bin/foo
  950. numprocs = 2
  951. priority = 1
  952. [fcgi-program:bar]
  953. socket = unix:///tmp/%(program_name)s.sock
  954. process_name = %(program_name)s_%(process_num)s
  955. command = /bin/bar
  956. user = testuser
  957. numprocs = 3
  958. [fcgi-program:flub]
  959. socket = unix:///tmp/%(program_name)s.sock
  960. command = /bin/flub
  961. [fcgi-program:cub]
  962. socket = tcp://localhost:6000
  963. command = /bin/cub
  964. """)
  965. from supervisor.options import UnhosedConfigParser
  966. config = UnhosedConfigParser()
  967. config.read_string(text)
  968. instance = self._makeOne()
  969. #Patch pwd and grp module functions to give us sentinel
  970. #uid/gid values so that the test does not depend on
  971. #any specific system users
  972. pwd_mock = Mock()
  973. pwd_mock.return_value = (None, None, sentinel.uid, sentinel.gid)
  974. grp_mock = Mock()
  975. grp_mock.return_value = (None, None, sentinel.gid)
  976. @patch('pwd.getpwuid', pwd_mock)
  977. @patch('pwd.getpwnam', pwd_mock)
  978. @patch('grp.getgrnam', grp_mock)
  979. def get_process_groups(instance, config):
  980. return instance.process_groups_from_parser(config)
  981. gconfigs = get_process_groups(instance, config)
  982. exp_owner = (sentinel.uid, sentinel.gid)
  983. self.assertEqual(len(gconfigs), 4)
  984. gconf_foo = gconfigs[0]
  985. self.assertEqual(gconf_foo.__class__, FastCGIGroupConfig)
  986. self.assertEqual(gconf_foo.name, 'foo')
  987. self.assertEqual(gconf_foo.priority, 1)
  988. self.assertEqual(gconf_foo.socket_config.url,
  989. 'unix:///tmp/foo.sock')
  990. self.assertEqual(exp_owner, gconf_foo.socket_config.get_owner())
  991. self.assertEqual(0666, gconf_foo.socket_config.get_mode())
  992. self.assertEqual(len(gconf_foo.process_configs), 2)
  993. pconfig_foo = gconf_foo.process_configs[0]
  994. self.assertEqual(pconfig_foo.__class__, FastCGIProcessConfig)
  995. gconf_bar = gconfigs[1]
  996. self.assertEqual(gconf_bar.name, 'bar')
  997. self.assertEqual(gconf_bar.priority, 999)
  998. self.assertEqual(gconf_bar.socket_config.url,
  999. 'unix:///tmp/bar.sock')
  1000. self.assertEqual(exp_owner, gconf_bar.socket_config.get_owner())
  1001. self.assertEqual(0700, gconf_bar.socket_config.get_mode())
  1002. self.assertEqual(len(gconf_bar.process_configs), 3)
  1003. gconf_cub = gconfigs[2]
  1004. self.assertEqual(gconf_cub.name, 'cub')
  1005. self.assertEqual(gconf_cub.socket_config.url,
  1006. 'tcp://localhost:6000')
  1007. self.assertEqual(len(gconf_cub.process_configs), 1)
  1008. gconf_flub = gconfigs[3]
  1009. self.assertEqual(gconf_flub.name, 'flub')
  1010. self.assertEqual(gconf_flub.socket_config.url,
  1011. 'unix:///tmp/flub.sock')
  1012. self.assertEqual(None, gconf_flub.socket_config.get_owner())
  1013. self.assertEqual(0700, gconf_flub.socket_config.get_mode())
  1014. self.assertEqual(len(gconf_flub.process_configs), 1)
  1015. def test_fcgi_program_no_socket(self):
  1016. text = lstrip("""\
  1017. [fcgi-program:foo]
  1018. process_name = %(program_name)s_%(process_num)s
  1019. command = /bin/foo
  1020. numprocs = 2
  1021. priority = 1
  1022. """)
  1023. from supervisor.options import UnhosedConfigParser
  1024. config = UnhosedConfigParser()
  1025. config.read_string(text)
  1026. instance = self._makeOne()
  1027. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1028. def test_fcgi_program_unknown_socket_protocol(self):
  1029. text = lstrip("""\
  1030. [fcgi-program:foo]
  1031. socket=junk://blah
  1032. process_name = %(program_name)s_%(process_num)s
  1033. command = /bin/foo
  1034. numprocs = 2
  1035. priority = 1
  1036. """)
  1037. from supervisor.options import UnhosedConfigParser
  1038. config = UnhosedConfigParser()
  1039. config.read_string(text)
  1040. instance = self._makeOne()
  1041. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1042. def test_fcgi_program_rel_unix_sock_path(self):
  1043. text = lstrip("""\
  1044. [fcgi-program:foo]
  1045. socket=unix://relative/path
  1046. process_name = %(program_name)s_%(process_num)s
  1047. command = /bin/foo
  1048. numprocs = 2
  1049. priority = 1
  1050. """)
  1051. from supervisor.options import UnhosedConfigParser
  1052. config = UnhosedConfigParser()
  1053. config.read_string(text)
  1054. instance = self._makeOne()
  1055. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1056. def test_fcgi_program_bad_tcp_sock_format(self):
  1057. text = lstrip("""\
  1058. [fcgi-program:foo]
  1059. socket=tcp://missingport
  1060. process_name = %(program_name)s_%(process_num)s
  1061. command = /bin/foo
  1062. numprocs = 2
  1063. priority = 1
  1064. """)
  1065. from supervisor.options import UnhosedConfigParser
  1066. config = UnhosedConfigParser()
  1067. config.read_string(text)
  1068. instance = self._makeOne()
  1069. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1070. def test_fcgi_program_bad_expansion_proc_num(self):
  1071. text = lstrip("""\
  1072. [fcgi-program:foo]
  1073. socket=unix:///tmp/%(process_num)s.sock
  1074. process_name = %(program_name)s_%(process_num)s
  1075. command = /bin/foo
  1076. numprocs = 2
  1077. priority = 1
  1078. """)
  1079. from supervisor.options import UnhosedConfigParser
  1080. config = UnhosedConfigParser()
  1081. config.read_string(text)
  1082. instance = self._makeOne()
  1083. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1084. def test_fcgi_program_socket_owner_set_for_tcp(self):
  1085. text = lstrip("""\
  1086. [fcgi-program:foo]
  1087. socket=tcp://localhost:8000
  1088. socket_owner=nobody:nobody
  1089. command = /bin/foo
  1090. """)
  1091. from supervisor.options import UnhosedConfigParser
  1092. config = UnhosedConfigParser()
  1093. config.read_string(text)
  1094. instance = self._makeOne()
  1095. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1096. def test_fcgi_program_socket_mode_set_for_tcp(self):
  1097. text = lstrip("""\
  1098. [fcgi-program:foo]
  1099. socket = tcp://localhost:8000
  1100. socket_mode = 0777
  1101. command = /bin/foo
  1102. """)
  1103. from supervisor.options import UnhosedConfigParser
  1104. config = UnhosedConfigParser()
  1105. config.read_string(text)
  1106. instance = self._makeOne()
  1107. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1108. def test_fcgi_program_bad_socket_owner(self):
  1109. text = lstrip("""\
  1110. [fcgi-program:foo]
  1111. socket = unix:///tmp/foo.sock
  1112. socket_owner = sometotaljunkuserthatshouldnobethere
  1113. command = /bin/foo
  1114. """)
  1115. from supervisor.options import UnhosedConfigParser
  1116. config = UnhosedConfigParser()
  1117. config.read_string(text)
  1118. instance = self._makeOne()
  1119. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1120. def test_fcgi_program_bad_socket_mode(self):
  1121. text = lstrip("""\
  1122. [fcgi-program:foo]
  1123. socket = unix:///tmp/foo.sock
  1124. socket_mode = junk
  1125. command = /bin/foo
  1126. """)
  1127. from supervisor.options import UnhosedConfigParser
  1128. config = UnhosedConfigParser()
  1129. config.read_string(text)
  1130. instance = self._makeOne()
  1131. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1132. def test_heterogeneous_process_groups_from_parser(self):
  1133. text = lstrip("""\
  1134. [program:one]
  1135. command = /bin/cat
  1136. [program:two]
  1137. command = /bin/cat
  1138. [group:thegroup]
  1139. programs = one,two
  1140. priority = 5
  1141. """)
  1142. from supervisor.options import UnhosedConfigParser
  1143. config = UnhosedConfigParser()
  1144. config.read_string(text)
  1145. instance = self._makeOne()
  1146. gconfigs = instance.process_groups_from_parser(config)
  1147. self.assertEqual(len(gconfigs), 1)
  1148. gconfig = gconfigs[0]
  1149. self.assertEqual(gconfig.name, 'thegroup')
  1150. self.assertEqual(gconfig.priority, 5)
  1151. self.assertEqual(len(gconfig.process_configs), 2)
  1152. def test_mixed_process_groups_from_parser1(self):
  1153. text = lstrip("""\
  1154. [program:one]
  1155. command = /bin/cat
  1156. [program:two]
  1157. command = /bin/cat
  1158. [program:many]
  1159. process_name = %(program_name)s_%(process_num)s
  1160. command = /bin/cat
  1161. numprocs = 2
  1162. priority = 1
  1163. [group:thegroup]
  1164. programs = one,two
  1165. priority = 5
  1166. """)
  1167. from supervisor.options import UnhosedConfigParser
  1168. config = UnhosedConfigParser()
  1169. config.read_string(text)
  1170. instance = self._makeOne()
  1171. gconfigs = instance.process_groups_from_parser(config)
  1172. self.assertEqual(len(gconfigs), 2)
  1173. manyconfig = gconfigs[0]
  1174. self.assertEqual(manyconfig.name, 'many')
  1175. self.assertEqual(manyconfig.priority, 1)
  1176. self.assertEqual(len(manyconfig.process_configs), 2)
  1177. gconfig = gconfigs[1]
  1178. self.assertEqual(gconfig.name, 'thegroup')
  1179. self.assertEqual(gconfig.priority, 5)
  1180. self.assertEqual(len(gconfig.process_configs), 2)
  1181. def test_mixed_process_groups_from_parser2(self):
  1182. text = lstrip("""\
  1183. [program:one]
  1184. command = /bin/cat
  1185. [program:two]
  1186. command = /bin/cat
  1187. [program:many]
  1188. process_name = %(program_name)s_%(process_num)s
  1189. command = /bin/cat
  1190. numprocs = 2
  1191. priority = 1
  1192. [group:thegroup]
  1193. programs = one,two, many
  1194. priority = 5
  1195. """)
  1196. from supervisor.options import UnhosedConfigParser
  1197. config = UnhosedConfigParser()
  1198. config.read_string(text)
  1199. instance = self._makeOne()
  1200. gconfigs = instance.process_groups_from_parser(config)
  1201. self.assertEqual(len(gconfigs), 1)
  1202. gconfig = gconfigs[0]
  1203. self.assertEqual(gconfig.name, 'thegroup')
  1204. self.assertEqual(gconfig.priority, 5)
  1205. self.assertEqual(len(gconfig.process_configs), 4)
  1206. def test_unknown_program_in_heterogeneous_group(self):
  1207. text = lstrip("""\
  1208. [program:one]
  1209. command = /bin/cat
  1210. [group:foo]
  1211. programs = notthere
  1212. """)
  1213. from supervisor.options import UnhosedConfigParser
  1214. config = UnhosedConfigParser()
  1215. config.read_string(text)
  1216. instance = self._makeOne()
  1217. self.assertRaises(ValueError, instance.process_groups_from_parser,
  1218. config)
  1219. def test_rpcinterfaces_from_parser(self):
  1220. text = lstrip("""\
  1221. [rpcinterface:dummy]
  1222. supervisor.rpcinterface_factory = %s
  1223. foo = bar
  1224. """ % __name__)
  1225. from supervisor.options import UnhosedConfigParser
  1226. config = UnhosedConfigParser()
  1227. config.read_string(text)
  1228. instance = self._makeOne()
  1229. factories = instance.get_plugins(config,
  1230. 'supervisor.rpcinterface_factory',
  1231. 'rpcinterface:')
  1232. self.assertEqual(len(factories), 1)
  1233. factory = factories[0]
  1234. self.assertEqual(factory[0], 'dummy')
  1235. self.assertEqual(factory[1], sys.modules[__name__])
  1236. self.assertEqual(factory[2], {'foo':'bar'})
  1237. def test_clear_autochildlogdir(self):
  1238. dn = tempfile.mkdtemp()
  1239. try:
  1240. instance = self._makeOne()
  1241. instance.childlogdir = dn
  1242. sid = 'supervisor'
  1243. instance.identifier = sid
  1244. logfn = instance.get_autochildlog_name('foo', sid,'stdout')
  1245. first = logfn + '.1'
  1246. second = logfn + '.2'
  1247. open(first, 'w')
  1248. open(second, 'w')
  1249. instance.clear_autochildlogdir()
  1250. self.failIf(os.path.exists(logfn))
  1251. self.failIf(os.path.exists(first))
  1252. self.failIf(os.path.exists(second))
  1253. finally:
  1254. shutil.rmtree(dn)
  1255. def test_clear_autochildlog_oserror(self):
  1256. instance = self._makeOne()
  1257. instance.childlogdir = '/tmp/this/cant/possibly/existjjjj'
  1258. instance.logger = DummyLogger()
  1259. instance.clear_autochildlogdir()
  1260. self.assertEqual(instance.logger.data, ['Could not clear childlog dir'])
  1261. def test_openhttpservers_reports_friendly_usage_when_eaddrinuse(self):
  1262. supervisord = DummySupervisor()
  1263. instance = self._makeOne()
  1264. def raise_eaddrinuse(supervisord):
  1265. raise socket.error(errno.EADDRINUSE)
  1266. instance.make_http_servers = raise_eaddrinuse
  1267. recorder = []
  1268. def record_usage(message):
  1269. recorder.append(message)
  1270. instance.usage = record_usage
  1271. instance.openhttpservers(supervisord)
  1272. self.assertEqual(len(recorder), 1)
  1273. expected = 'Another program is already listening'
  1274. self.assertTrue(recorder[0].startswith(expected))
  1275. def test_openhttpservers_reports_socket_error_with_errno(self):
  1276. supervisord = DummySupervisor()
  1277. instance = self._makeOne()
  1278. def make_http_servers(supervisord):
  1279. raise socket.error(errno.EPERM)
  1280. instance.make_http_servers = make_http_servers
  1281. recorder = []
  1282. def record_usage(message):
  1283. recorder.append(message)
  1284. instance.usage = record_usage
  1285. instance.openhttpservers(supervisord)
  1286. self.assertEqual(len(recorder), 1)
  1287. expected = ('Cannot open an HTTP server: socket.error '
  1288. 'reported errno.EPERM (%d)' % errno.EPERM)
  1289. self.assertEqual(recorder[0], expected)
  1290. def test_openhttpservers_reports_other_socket_errors(self):
  1291. supervisord = DummySupervisor()
  1292. instance = self._makeOne()
  1293. def make_http_servers(supervisord):
  1294. raise socket.error('uh oh')
  1295. instance.make_http_servers = make_http_servers
  1296. recorder = []
  1297. def record_usage(message):
  1298. recorder.append(message)
  1299. instance.usage = record_usage
  1300. instance.openhttpservers(supervisord)
  1301. self.assertEqual(len(recorder), 1)
  1302. expected = ('Cannot open an HTTP server: socket.error '
  1303. 'reported uh oh')
  1304. self.assertEqual(recorder[0], expected)
  1305. def test_openhttpservers_reports_value_errors(self):
  1306. supervisord = DummySupervisor()
  1307. instance = self._makeOne()
  1308. def make_http_servers(supervisord):
  1309. raise ValueError('not prefixed with help')
  1310. instance.make_http_servers = make_http_servers
  1311. recorder = []
  1312. def record_usage(message):
  1313. recorder.append(message)
  1314. instance.usage = record_usage
  1315. instance.openhttpservers(supervisord)
  1316. self.assertEqual(len(recorder), 1)
  1317. expected = 'not prefixed with help'
  1318. self.assertEqual(recorder[0], expected)
  1319. def test_openhttpservers_does_not_catch_other_exception_types(self):
  1320. supervisord = DummySupervisor()
  1321. instance = self._makeOne()
  1322. def make_http_servers(supervisord):
  1323. raise OverflowError
  1324. instance.make_http_servers = make_http_servers
  1325. # this scenario probably means a bug in supervisor. we dump
  1326. # all the gory details on the poor user for troubleshooting
  1327. self.assertRaises(OverflowError,
  1328. instance.openhttpservers, supervisord)
  1329. def test_dropPrivileges_user_none(self):
  1330. instance = self._makeOne()
  1331. msg = instance.dropPrivileges(None)
  1332. self.assertEqual(msg, "No user specified to setuid to!")
  1333. @patch('pwd.getpwuid', Mock(return_value=["foo", None, 12, 34]))
  1334. @patch('os.getuid', Mock(return_value=12))
  1335. def test_dropPrivileges_nonroot_same_user(self):
  1336. instance = self._makeOne()
  1337. msg = instance.dropPrivileges(os.getuid())
  1338. self.assertEqual(msg, None) # no error if same user
  1339. @patch('pwd.getpwuid', Mock(return_value=["foo", None, 55, 34]))
  1340. @patch('os.getuid', Mock(return_value=12))
  1341. def test_dropPrivileges_nonroot_different_user(self):
  1342. instance = self._makeOne()
  1343. msg = instance.dropPrivileges(42)
  1344. self.assertEqual(msg, "Can't drop privilege as nonroot user")
  1345. class TestProcessConfig(unittest.TestCase):
  1346. def _getTargetClass(self):
  1347. from supervisor.options import ProcessConfig
  1348. return ProcessConfig
  1349. def _makeOne(self, *arg, **kw):
  1350. defaults = {}
  1351. for name in ('name', 'command', 'directory', 'umask',
  1352. 'priority', 'autostart', 'autorestart',
  1353. 'startsecs', 'startretries', 'uid',
  1354. 'stdout_logfile', 'stdout_capture_maxbytes',
  1355. 'stdout_events_enabled',
  1356. 'stdout_logfile_backups', 'stdout_logfile_maxbytes',
  1357. 'stderr_logfile', 'stderr_capture_maxbytes',
  1358. 'stderr_events_enabled',
  1359. 'stderr_logfile_backups', 'stderr_logfile_maxbytes',
  1360. 'stopsignal', 'stopwaitsecs', 'stopasgroup', 'killasgroup', 'exitcodes',
  1361. 'redirect_stderr', 'environment'):
  1362. defaults[name] = name
  1363. defaults.update(kw)
  1364. return self._getTargetClass()(*arg, **defaults)
  1365. def test_create_autochildlogs(self):
  1366. options = DummyOptions()
  1367. instance = self._makeOne(options)
  1368. from supervisor.datatypes import Automatic
  1369. instance.stdout_logfile = Automatic
  1370. instance.stderr_logfile = Automatic
  1371. instance.create_autochildlogs()
  1372. self.assertEqual(instance.stdout_logfile, options.tempfile_name)
  1373. self.assertEqual(instance.stderr_logfile, options.tempfile_name)
  1374. def test_make_process(self):
  1375. options = DummyOptions()
  1376. instance = self._makeOne(options)
  1377. process = instance.make_process()
  1378. from supervisor.process import Subprocess
  1379. self.assertEqual(process.__class__, Subprocess)
  1380. self.assertEqual(process.group, None)
  1381. def test_make_process_with_group(self):
  1382. options = DummyOptions()
  1383. instance = self._makeOne(options)
  1384. process = instance.make_process('abc')
  1385. from supervisor.process import Subprocess
  1386. self.assertEqual(process.__class__, Subprocess)
  1387. self.assertEqual(process.group, 'abc')
  1388. def test_make_dispatchers_stderr_not_redirected(self):
  1389. options = DummyOptions()
  1390. instance = self._makeOne(options)
  1391. instance.redirect_stderr = False
  1392. process1 = DummyProcess(instance)
  1393. dispatchers, pipes = instance.make_dispatchers(process1)
  1394. self.assertEqual(dispatchers[5].channel, 'stdout')
  1395. from supervisor.events import ProcessCommunicationStdoutEvent
  1396. self.assertEqual(dispatchers[5].event_type,
  1397. ProcessCommunicationStdoutEvent)
  1398. self.assertEqual(pipes['stdout'], 5)
  1399. self.assertEqual(dispatchers[7].channel, 'stderr')
  1400. from supervisor.events import ProcessCommunicationStderrEvent
  1401. self.assertEqual(dispatchers[7].event_type,
  1402. ProcessCommunicationStderrEvent)
  1403. self.assertEqual(pipes['stderr'], 7)
  1404. def test_make_dispatchers_stderr_redirected(self):
  1405. options = DummyOptions()
  1406. instance = self._makeOne(options)
  1407. process1 = DummyProcess(instance)
  1408. dispatchers, pipes = instance.make_dispatchers(process1)
  1409. self.assertEqual(dispatchers[5].channel, 'stdout')
  1410. self.assertEqual(pipes['stdout'], 5)
  1411. self.assertEqual(pipes['stderr'], None)
  1412. class FastCGIProcessConfigTest(unittest.TestCase):
  1413. def _getTargetClass(self):
  1414. from supervisor.options import FastCGIProcessConfig
  1415. return FastCGIProcessConfig
  1416. def _makeOne(self, *arg, **kw):
  1417. defaults = {}
  1418. for name in ('name', 'command', 'directory', 'umask',
  1419. 'priority', 'autostart', 'autorestart',
  1420. 'startsecs', 'startretries', 'uid',
  1421. 'stdout_logfile', 'stdout_capture_maxbytes',
  1422. 'stdout_events_enabled',
  1423. 'stdout_logfile_backups', 'stdout_logfile_maxbytes',
  1424. 'stderr_logfile', 'stderr_capture_maxbytes',
  1425. 'stderr_events_enabled',
  1426. 'stderr_logfile_backups', 'stderr_logfile_maxbytes',
  1427. 'stopsignal', 'stopwaitsecs', 'stopasgroup', 'killasgroup', 'exitcodes',
  1428. 'redirect_stderr', 'environment'):
  1429. defaults[name] = name
  1430. defaults.update(kw)
  1431. return self._getTargetClass()(*arg, **defaults)
  1432. def test_make_process(self):
  1433. options = DummyOptions()
  1434. instance = self._makeOne(options)
  1435. self.assertRaises(NotImplementedError, instance.make_process)
  1436. def test_make_process_with_group(self):
  1437. options = DummyOptions()
  1438. instance = self._makeOne(options)
  1439. process = instance.make_process('abc')
  1440. from supervisor.process import FastCGISubprocess
  1441. self.assertEqual(process.__class__, FastCGISubprocess)
  1442. self.assertEqual(process.group, 'abc')
  1443. def test_make_dispatchers(self):
  1444. options = DummyOptions()
  1445. instance = self._makeOne(options)
  1446. instance.redirect_stderr = False
  1447. process1 = DummyProcess(instance)
  1448. dispatchers, pipes = instance.make_dispatchers(process1)
  1449. self.assertEqual(dispatchers[4].channel, 'stdin')
  1450. self.assertEqual(dispatchers[4].closed, True)
  1451. self.assertEqual(dispatchers[5].channel, 'stdout')
  1452. from supervisor.events import ProcessCommunicationStdoutEvent
  1453. self.assertEqual(dispatchers[5].event_type,
  1454. ProcessCommunicationStdoutEvent)
  1455. self.assertEqual(pipes['stdout'], 5)
  1456. self.assertEqual(dispatchers[7].channel, 'stderr')
  1457. from supervisor.events import ProcessCommunicationStderrEvent
  1458. self.assertEqual(dispatchers[7].event_type,
  1459. ProcessCommunicationStderrEvent)
  1460. self.assertEqual(pipes['stderr'], 7)
  1461. class ProcessGroupConfigTests(unittest.TestCase):
  1462. def _getTargetClass(self):
  1463. from supervisor.options import ProcessGroupConfig
  1464. return ProcessGroupConfig
  1465. def _makeOne(self, options, name, priority, pconfigs):
  1466. return self._getTargetClass()(options, name, priority, pconfigs)
  1467. def test_ctor(self):
  1468. options = DummyOptions()
  1469. instance = self._makeOne(options, 'whatever', 999, [])
  1470. self.assertEqual(instance.options, options)
  1471. self.assertEqual(instance.name, 'whatever')
  1472. self.assertEqual(instance.priority, 999)
  1473. self.assertEqual(instance.process_configs, [])
  1474. def test_after_setuid(self):
  1475. options = DummyOptions()
  1476. pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
  1477. instance = self._makeOne(options, 'whatever', 999, pconfigs)
  1478. instance.after_setuid()
  1479. self.assertEqual(pconfigs[0].autochildlogs_created, True)
  1480. def test_make_group(self):
  1481. options = DummyOptions()
  1482. pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
  1483. instance = self._makeOne(options, 'whatever', 999, pconfigs)
  1484. group = instance.make_group()
  1485. from supervisor.process import ProcessGroup
  1486. self.assertEqual(group.__class__, ProcessGroup)
  1487. class FastCGIGroupConfigTests(unittest.TestCase):
  1488. def _getTargetClass(self):
  1489. from supervisor.options import FastCGIGroupConfig
  1490. return FastCGIGroupConfig
  1491. def _makeOne(self, *args, **kw):
  1492. return self._getTargetClass()(*args, **kw)
  1493. def test_ctor(self):
  1494. options = DummyOptions()
  1495. sock_config = DummySocketConfig(6)
  1496. instance = self._makeOne(options, 'whatever', 999, [], sock_config)
  1497. self.assertEqual(instance.options, options)
  1498. self.assertEqual(instance.name, 'whatever')
  1499. self.assertEqual(instance.priority, 999)
  1500. self.assertEqual(instance.process_configs, [])
  1501. self.assertEqual(instance.socket_config, sock_config)
  1502. def test_same_sockets_are_equal(self):
  1503. options = DummyOptions()
  1504. sock_config1 = DummySocketConfig(6)
  1505. instance1 = self._makeOne(options, 'whatever', 999, [], sock_config1)
  1506. sock_config2 = DummySocketConfig(6)
  1507. instance2 = self._makeOne(options, 'whatever', 999, [], sock_config2)
  1508. self.assertTrue(instance1 == instance2)
  1509. self.assertFalse(instance1 != instance2)
  1510. def test_diff_sockets_are_not_equal(self):
  1511. options = DummyOptions()
  1512. sock_config1 = DummySocketConfig(6)
  1513. instance1 = self._makeOne(options, 'whatever', 999, [], sock_config1)
  1514. sock_config2 = DummySocketConfig(7)
  1515. instance2 = self._makeOne(options, 'whatever', 999, [], sock_config2)
  1516. self.assertTrue(instance1 != instance2)
  1517. self.assertFalse(instance1 == instance2)
  1518. class SignalReceiverTests(unittest.TestCase):
  1519. def test_returns_None_initially(self):
  1520. from supervisor.options import SignalReceiver
  1521. sr = SignalReceiver()
  1522. self.assertEquals(sr.get_signal(), None)
  1523. def test_returns_signals_in_order_received(self):
  1524. from supervisor.options import SignalReceiver
  1525. sr = SignalReceiver()
  1526. sr.receive(signal.SIGTERM, 'frame')
  1527. sr.receive(signal.SIGCHLD, 'frame')
  1528. self.assertEquals(sr.get_signal(), signal.SIGTERM)
  1529. self.assertEquals(sr.get_signal(), signal.SIGCHLD)
  1530. self.assertEquals(sr.get_signal(), None)
  1531. def test_does_not_queue_duplicate_signals(self):
  1532. from supervisor.options import SignalReceiver
  1533. sr = SignalReceiver()
  1534. sr.receive(signal.SIGTERM, 'frame')
  1535. sr.receive(signal.SIGTERM, 'frame')
  1536. self.assertEquals(sr.get_signal(), signal.SIGTERM)
  1537. self.assertEquals(sr.get_signal(), None)
  1538. def test_queues_again_after_being_emptied(self):
  1539. from supervisor.options import SignalReceiver
  1540. sr = SignalReceiver()
  1541. sr.receive(signal.SIGTERM, 'frame')
  1542. self.assertEquals(sr.get_signal(), signal.SIGTERM)
  1543. self.assertEquals(sr.get_signal(), None)
  1544. sr.receive(signal.SIGCHLD, 'frame')
  1545. self.assertEquals(sr.get_signal(), signal.SIGCHLD)
  1546. self.assertEquals(sr.get_signal(), None)
  1547. class UtilFunctionsTests(unittest.TestCase):
  1548. def test_make_namespec(self):
  1549. from supervisor.options import make_namespec
  1550. self.assertEquals(make_namespec('group', 'process'), 'group:process')
  1551. self.assertEquals(make_namespec('process', 'process'), 'process')
  1552. def test_split_namespec(self):
  1553. from supervisor.options import split_namespec
  1554. s = split_namespec
  1555. self.assertEquals(s('process:group'), ('process', 'group'))
  1556. self.assertEquals(s('process'), ('process', 'process'))
  1557. self.assertEquals(s('group:'), ('group', None))
  1558. self.assertEquals(s('group:*'), ('group', None))
  1559. def test_suite():
  1560. return unittest.findTestCases(sys.modules[__name__])
  1561. if __name__ == '__main__':
  1562. unittest.main(defaultTest='test_suite')