test_options.py 60 KB

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