test_options.py 101 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698
  1. """Test suite for supervisor.options"""
  2. import logging
  3. import os
  4. import sys
  5. import tempfile
  6. import socket
  7. import unittest
  8. import signal
  9. import shutil
  10. import errno
  11. from supervisor.compat import StringIO
  12. from supervisor.compat import as_bytes
  13. from supervisor.tests.base import Mock, sentinel, patch
  14. from supervisor.loggers import LevelsByName
  15. from supervisor.tests.base import DummySupervisor
  16. from supervisor.tests.base import DummyLogger
  17. from supervisor.tests.base import DummyOptions
  18. from supervisor.tests.base import DummyPConfig
  19. from supervisor.tests.base import DummyProcess
  20. from supervisor.tests.base import DummySocketConfig
  21. from supervisor.tests.base import lstrip
  22. class DummyExitException(Exception):
  23. def __init__(self, exitcode):
  24. self.exitcode = exitcode
  25. def dummy_exit():
  26. """Dummy out exit so we don't actually exit"""
  27. def dummy_exit(_exitcode=0):
  28. raise DummyExitException(exitcode=_exitcode)
  29. return dummy_exit
  30. def _getTempFile(name):
  31. prefix = 'supervisor.{0}.'.format(name)
  32. return tempfile.NamedTemporaryFile(prefix=prefix)
  33. logger = logging.getLogger(__name__)
  34. class OptionTests(unittest.TestCase):
  35. def _getTargetClass(self):
  36. from supervisor.options import Options
  37. return Options
  38. def _makeOptions(self, read_error=False):
  39. Options = self._getTargetClass()
  40. from supervisor.datatypes import integer
  41. class MyOptions(Options):
  42. master = {
  43. 'other': 41 }
  44. def __init__(self, read_error=read_error):
  45. self.read_error = read_error
  46. Options.__init__(self)
  47. class Foo(object): pass
  48. self.configroot = Foo()
  49. def read_config(self, fp):
  50. if self.read_error:
  51. raise ValueError(self.read_error)
  52. # Pretend we read it from file:
  53. self.configroot.__dict__.update(self.default_map)
  54. self.configroot.__dict__.update(self.master)
  55. options = MyOptions()
  56. options.configfile = StringIO()
  57. options.add(name='anoption', confname='anoption',
  58. short='o', long='option', default='default')
  59. options.add(name='other', confname='other', env='OTHER',
  60. short='p:', long='other=', handler=integer)
  61. return options
  62. def test_add_flag_not_None_handler_not_None(self):
  63. cls = self._getTargetClass()
  64. inst = cls()
  65. self.assertRaises(ValueError, inst.add, flag=True, handler=True)
  66. def test_add_flag_not_None_long_false_short_false(self):
  67. cls = self._getTargetClass()
  68. inst = cls()
  69. self.assertRaises(
  70. ValueError,
  71. inst.add,
  72. flag=True,
  73. long=False,
  74. short=False,
  75. )
  76. def test_add_flag_not_None_short_endswith_colon(self):
  77. cls = self._getTargetClass()
  78. inst = cls()
  79. self.assertRaises(
  80. ValueError,
  81. inst.add,
  82. flag=True,
  83. long=False,
  84. short=":",
  85. )
  86. def test_add_flag_not_None_long_endswith_equal(self):
  87. cls = self._getTargetClass()
  88. inst = cls()
  89. self.assertRaises(
  90. ValueError,
  91. inst.add,
  92. flag=True,
  93. long='=',
  94. short=False,
  95. )
  96. def test_add_inconsistent_short_long_options(self):
  97. cls = self._getTargetClass()
  98. inst = cls()
  99. self.assertRaises(
  100. ValueError,
  101. inst.add,
  102. long='=',
  103. short='abc',
  104. )
  105. def test_add_short_option_startswith_dash(self):
  106. cls = self._getTargetClass()
  107. inst = cls()
  108. self.assertRaises(
  109. ValueError,
  110. inst.add,
  111. long=False,
  112. short='-abc',
  113. )
  114. def test_add_short_option_too_long(self):
  115. cls = self._getTargetClass()
  116. inst = cls()
  117. self.assertRaises(
  118. ValueError,
  119. inst.add,
  120. long=False,
  121. short='abc',
  122. )
  123. def test_add_duplicate_short_option_key(self):
  124. cls = self._getTargetClass()
  125. inst = cls()
  126. inst.options_map = {'-a':True}
  127. self.assertRaises(
  128. ValueError,
  129. inst.add,
  130. long=False,
  131. short='a',
  132. )
  133. def test_add_long_option_startswith_dash(self):
  134. cls = self._getTargetClass()
  135. inst = cls()
  136. self.assertRaises(
  137. ValueError,
  138. inst.add,
  139. long='-abc',
  140. short=False,
  141. )
  142. def test_add_duplicate_long_option_key(self):
  143. cls = self._getTargetClass()
  144. inst = cls()
  145. inst.options_map = {'--abc':True}
  146. self.assertRaises(
  147. ValueError,
  148. inst.add,
  149. long='abc',
  150. short=False,
  151. )
  152. def test_searchpaths(self):
  153. options = self._makeOptions()
  154. self.assertEqual(len(options.searchpaths), 5)
  155. self.assertTrue('supervisord.conf' in options.searchpaths)
  156. self.assertTrue('etc/supervisord.conf' in options.searchpaths)
  157. self.assertTrue('/etc/supervisord.conf' in options.searchpaths)
  158. def test_options_and_args_order(self):
  159. # Only config file exists
  160. options = self._makeOptions()
  161. options.realize([])
  162. self.assertEqual(options.anoption, 'default')
  163. self.assertEqual(options.other, 41)
  164. # Env should trump config
  165. options = self._makeOptions()
  166. os.environ['OTHER'] = '42'
  167. options.realize([])
  168. self.assertEqual(options.other, 42)
  169. # Opt should trump both env (still set) and config
  170. options = self._makeOptions()
  171. options.realize(['-p', '43'])
  172. self.assertEqual(options.other, 43)
  173. del os.environ['OTHER']
  174. def test_config_reload(self):
  175. options = self._makeOptions()
  176. options.realize([])
  177. self.assertEqual(options.other, 41)
  178. options.master['other'] = 42
  179. options.process_config()
  180. self.assertEqual(options.other, 42)
  181. def test_config_reload_do_usage_false(self):
  182. options = self._makeOptions(read_error='error')
  183. self.assertRaises(ValueError, options.process_config,
  184. False)
  185. def test_config_reload_do_usage_true(self):
  186. options = self._makeOptions(read_error='error')
  187. L = []
  188. def exit(num):
  189. L.append(num)
  190. options.stderr = options.stdout = StringIO()
  191. options.exit = exit
  192. options.configroot.anoption = 1
  193. options.configroot.other = 1
  194. options.process_config(True)
  195. self.assertEqual(L, [2])
  196. def test__set(self):
  197. from supervisor.options import Options
  198. options = Options()
  199. options._set('foo', 'bar', 0)
  200. self.assertEqual(options.foo, 'bar')
  201. self.assertEqual(options.attr_priorities['foo'], 0)
  202. options._set('foo', 'baz', 1)
  203. self.assertEqual(options.foo, 'baz')
  204. self.assertEqual(options.attr_priorities['foo'], 1)
  205. options._set('foo', 'gazonk', 0)
  206. self.assertEqual(options.foo, 'baz')
  207. self.assertEqual(options.attr_priorities['foo'], 1)
  208. options._set('foo', 'gazonk', 1)
  209. self.assertEqual(options.foo, 'gazonk')
  210. def test_missing_default_config(self):
  211. options = self._makeOptions()
  212. options.searchpaths = []
  213. options.exit = dummy_exit()
  214. options.stderr = StringIO()
  215. try:
  216. options.default_configfile()
  217. except DummyExitException as e:
  218. self.assertEqual(e.exitcode, 2)
  219. else:
  220. self.fail("expected exception")
  221. msg = "Error: No config file found at default paths"
  222. self.assertTrue(options.stderr.getvalue().startswith(msg))
  223. def test_default_config(self):
  224. options = self._makeOptions()
  225. tempf = tempfile.NamedTemporaryFile()
  226. options.searchpaths = [tempf.name]
  227. config = options.default_configfile()
  228. self.assertEqual(config, tempf.name)
  229. tempf.close()
  230. def test_help(self):
  231. options = self._makeOptions()
  232. options.exit = dummy_exit()
  233. options.stdout = StringIO()
  234. options.progname = 'test_help'
  235. options.doc = 'A sample docstring for %s'
  236. self.assertRaises(DummyExitException,
  237. options.help, 'Argument ignored?')
  238. msg = 'A sample docstring for test_help\n'
  239. self.assertEqual(options.stdout.getvalue(), msg)
  240. class ClientOptionsTests(unittest.TestCase):
  241. def _getTargetClass(self):
  242. from supervisor.options import ClientOptions
  243. return ClientOptions
  244. def _makeOne(self):
  245. return self._getTargetClass()()
  246. def test_no_config_file(self):
  247. """Making sure config file is not required."""
  248. instance = self._makeOne()
  249. # No default config file search in case they would exist
  250. self.assertTrue(len(instance.searchpaths) > 0)
  251. instance.searchpaths = []
  252. instance.exit = dummy_exit()
  253. instance.realize(args=['-s', 'http://localhost:9001', '-u', 'chris',
  254. '-p', '123'])
  255. self.assertEqual(instance.interactive, 1)
  256. self.assertEqual(instance.serverurl, 'http://localhost:9001')
  257. self.assertEqual(instance.username, 'chris')
  258. self.assertEqual(instance.password, '123')
  259. def test_options(self):
  260. tempdir = tempfile.gettempdir()
  261. s = lstrip("""[supervisorctl]
  262. serverurl=http://localhost:9001
  263. username=chris
  264. password=123
  265. prompt=mysupervisor
  266. history_file=%s/sc_history
  267. """ % tempdir)
  268. fp = StringIO(s)
  269. instance = self._makeOne()
  270. instance.configfile = fp
  271. instance.realize(args=[])
  272. self.assertEqual(instance.interactive, True)
  273. history_file = os.path.join(tempdir, 'sc_history')
  274. self.assertEqual(instance.history_file, history_file)
  275. options = instance.configroot.supervisorctl
  276. self.assertEqual(options.prompt, 'mysupervisor')
  277. self.assertEqual(options.serverurl, 'http://localhost:9001')
  278. self.assertEqual(options.username, 'chris')
  279. self.assertEqual(options.password, '123')
  280. self.assertEqual(options.history_file, history_file)
  281. def test_options_with_environment_expansions(self):
  282. s = lstrip("""[supervisorctl]
  283. serverurl=http://localhost:%(ENV_SERVER_PORT)s
  284. username=%(ENV_CLIENT_USER)s
  285. password=%(ENV_CLIENT_PASS)s
  286. prompt=%(ENV_CLIENT_PROMPT)s
  287. history_file=/path/to/histdir/.supervisorctl%(ENV_CLIENT_HIST_EXT)s
  288. """)
  289. fp = StringIO(s)
  290. instance = self._makeOne()
  291. instance.environ_expansions = {'ENV_HOME': tempfile.gettempdir(),
  292. 'ENV_USER': 'johndoe',
  293. 'ENV_SERVER_PORT': '9210',
  294. 'ENV_CLIENT_USER': 'someuser',
  295. 'ENV_CLIENT_PASS': 'passwordhere',
  296. 'ENV_CLIENT_PROMPT': 'xsupervisor',
  297. 'ENV_CLIENT_HIST_EXT': '.hist',
  298. }
  299. instance.configfile = fp
  300. instance.realize(args=[])
  301. self.assertEqual(instance.interactive, True)
  302. options = instance.configroot.supervisorctl
  303. self.assertEqual(options.prompt, 'xsupervisor')
  304. self.assertEqual(options.serverurl, 'http://localhost:9210')
  305. self.assertEqual(options.username, 'someuser')
  306. self.assertEqual(options.password, 'passwordhere')
  307. self.assertEqual(options.history_file, '/path/to/histdir/.supervisorctl.hist')
  308. def test_read_config_not_found(self):
  309. instance = self._makeOne()
  310. def dummy_exists(fn):
  311. return False
  312. instance.exists = dummy_exists
  313. try:
  314. instance.read_config('filename')
  315. except ValueError as e:
  316. self.assertTrue("could not find config file" in str(e))
  317. else:
  318. self.fail("expected exception")
  319. def test_read_config_unreadable(self):
  320. instance = self._makeOne()
  321. def dummy_exists(fn):
  322. return True
  323. instance.exists = dummy_exists
  324. def dummy_open(fn, mode):
  325. raise IOError(errno.EACCES, 'Permission denied: %s' % fn)
  326. instance.open = dummy_open
  327. try:
  328. instance.read_config('filename')
  329. except ValueError as e:
  330. self.assertTrue("could not read config file" in str(e))
  331. else:
  332. self.fail("expected exception")
  333. def test_options_unixsocket_cli(self):
  334. fp = StringIO('[supervisorctl]')
  335. instance = self._makeOne()
  336. instance.configfile = fp
  337. instance.realize(args=['--serverurl', 'unix:///dev/null'])
  338. self.assertEqual(instance.serverurl, 'unix:///dev/null')
  339. def test_options_unixsocket_configfile(self):
  340. s = lstrip("""[supervisorctl]
  341. serverurl=unix:///dev/null
  342. """)
  343. fp = StringIO(s)
  344. instance = self._makeOne()
  345. instance.configfile = fp
  346. instance.realize(args=[])
  347. self.assertEqual(instance.serverurl, 'unix:///dev/null')
  348. class ServerOptionsTests(unittest.TestCase):
  349. def _getTargetClass(self):
  350. from supervisor.options import ServerOptions
  351. return ServerOptions
  352. def _makeOne(self):
  353. return self._getTargetClass()()
  354. def test_version(self):
  355. from supervisor.options import VERSION
  356. options = self._makeOne()
  357. options.stdout = StringIO()
  358. self.assertRaises(SystemExit, options.version, None)
  359. self.assertEqual(options.stdout.getvalue(), VERSION + '\n')
  360. def test_options(self):
  361. s = lstrip("""[inet_http_server]
  362. port=127.0.0.1:8999
  363. username=chrism
  364. password=foo
  365. [supervisord]
  366. directory=%(tempdir)s
  367. backofflimit=10
  368. user=root
  369. umask=022
  370. logfile=supervisord.log
  371. logfile_maxbytes=1000MB
  372. logfile_backups=5
  373. loglevel=error
  374. pidfile=supervisord.pid
  375. nodaemon=true
  376. identifier=fleeb
  377. childlogdir=%(tempdir)s
  378. nocleanup=true
  379. minfds=2048
  380. minprocs=300
  381. environment=FAKE_ENV_VAR=/some/path
  382. [program:cat1]
  383. command=/bin/cat
  384. priority=1
  385. autostart=true
  386. user=root
  387. stdout_logfile=/tmp/cat.log
  388. stopsignal=KILL
  389. stopwaitsecs=5
  390. startsecs=5
  391. startretries=10
  392. directory=/tmp
  393. umask=002
  394. [program:cat2]
  395. priority=2
  396. command=/bin/cat
  397. autostart=true
  398. autorestart=false
  399. stdout_logfile_maxbytes = 1024
  400. stdout_logfile_backups = 2
  401. stdout_logfile = /tmp/cat2.log
  402. [program:cat3]
  403. priority=3
  404. process_name = replaced
  405. command=/bin/cat
  406. autorestart=true
  407. exitcodes=0,1,127
  408. stopasgroup=true
  409. killasgroup=true
  410. [program:cat4]
  411. priority=4
  412. process_name = fleeb_%%(process_num)s
  413. numprocs = 2
  414. command = /bin/cat
  415. autorestart=unexpected
  416. [program:cat5]
  417. priority=5
  418. process_name = foo_%%(process_num)02d
  419. numprocs = 2
  420. numprocs_start = 1
  421. command = /bin/cat
  422. directory = /some/path/foo_%%(process_num)02d
  423. """ % {'tempdir':tempfile.gettempdir()})
  424. from supervisor import datatypes
  425. fp = StringIO(s)
  426. instance = self._makeOne()
  427. instance.configfile = fp
  428. instance.realize(args=[])
  429. options = instance.configroot.supervisord
  430. self.assertEqual(options.directory, tempfile.gettempdir())
  431. self.assertEqual(options.umask, 18) # 022 in Py2, 0o22 in Py3
  432. self.assertEqual(options.logfile, 'supervisord.log')
  433. self.assertEqual(options.logfile_maxbytes, 1000 * 1024 * 1024)
  434. self.assertEqual(options.logfile_backups, 5)
  435. self.assertEqual(options.loglevel, 40)
  436. self.assertEqual(options.pidfile, 'supervisord.pid')
  437. self.assertEqual(options.nodaemon, True)
  438. self.assertEqual(options.identifier, 'fleeb')
  439. self.assertEqual(options.childlogdir, tempfile.gettempdir())
  440. self.assertEqual(len(options.server_configs), 1)
  441. self.assertEqual(options.server_configs[0]['family'], socket.AF_INET)
  442. self.assertEqual(options.server_configs[0]['host'], '127.0.0.1')
  443. self.assertEqual(options.server_configs[0]['port'], 8999)
  444. self.assertEqual(options.server_configs[0]['username'], 'chrism')
  445. self.assertEqual(options.server_configs[0]['password'], 'foo')
  446. self.assertEqual(options.nocleanup, True)
  447. self.assertEqual(options.minfds, 2048)
  448. self.assertEqual(options.minprocs, 300)
  449. self.assertEqual(options.nocleanup, True)
  450. self.assertEqual(len(options.process_group_configs), 5)
  451. self.assertEqual(options.environment, dict(FAKE_ENV_VAR='/some/path'))
  452. cat1 = options.process_group_configs[0]
  453. self.assertEqual(cat1.name, 'cat1')
  454. self.assertEqual(cat1.priority, 1)
  455. self.assertEqual(len(cat1.process_configs), 1)
  456. proc1 = cat1.process_configs[0]
  457. self.assertEqual(proc1.name, 'cat1')
  458. self.assertEqual(proc1.command, '/bin/cat')
  459. self.assertEqual(proc1.priority, 1)
  460. self.assertEqual(proc1.autostart, True)
  461. self.assertEqual(proc1.autorestart, datatypes.RestartWhenExitUnexpected)
  462. self.assertEqual(proc1.startsecs, 5)
  463. self.assertEqual(proc1.startretries, 10)
  464. self.assertEqual(proc1.uid, 0)
  465. self.assertEqual(proc1.stdout_logfile, '/tmp/cat.log')
  466. self.assertEqual(proc1.stopsignal, signal.SIGKILL)
  467. self.assertEqual(proc1.stopwaitsecs, 5)
  468. self.assertEqual(proc1.stopasgroup, False)
  469. self.assertEqual(proc1.killasgroup, False)
  470. self.assertEqual(proc1.stdout_logfile_maxbytes,
  471. datatypes.byte_size('50MB'))
  472. self.assertEqual(proc1.stdout_logfile_backups, 10)
  473. self.assertEqual(proc1.exitcodes, [0,2])
  474. self.assertEqual(proc1.directory, '/tmp')
  475. self.assertEqual(proc1.umask, 2)
  476. self.assertEqual(proc1.environment, dict(FAKE_ENV_VAR='/some/path'))
  477. cat2 = options.process_group_configs[1]
  478. self.assertEqual(cat2.name, 'cat2')
  479. self.assertEqual(cat2.priority, 2)
  480. self.assertEqual(len(cat2.process_configs), 1)
  481. proc2 = cat2.process_configs[0]
  482. self.assertEqual(proc2.name, 'cat2')
  483. self.assertEqual(proc2.command, '/bin/cat')
  484. self.assertEqual(proc2.priority, 2)
  485. self.assertEqual(proc2.autostart, True)
  486. self.assertEqual(proc2.autorestart, False)
  487. self.assertEqual(proc2.uid, None)
  488. self.assertEqual(proc2.stdout_logfile, '/tmp/cat2.log')
  489. self.assertEqual(proc2.stopsignal, signal.SIGTERM)
  490. self.assertEqual(proc2.stopasgroup, False)
  491. self.assertEqual(proc2.killasgroup, False)
  492. self.assertEqual(proc2.stdout_logfile_maxbytes, 1024)
  493. self.assertEqual(proc2.stdout_logfile_backups, 2)
  494. self.assertEqual(proc2.exitcodes, [0,2])
  495. self.assertEqual(proc2.directory, None)
  496. cat3 = options.process_group_configs[2]
  497. self.assertEqual(cat3.name, 'cat3')
  498. self.assertEqual(cat3.priority, 3)
  499. self.assertEqual(len(cat3.process_configs), 1)
  500. proc3 = cat3.process_configs[0]
  501. self.assertEqual(proc3.name, 'replaced')
  502. self.assertEqual(proc3.command, '/bin/cat')
  503. self.assertEqual(proc3.priority, 3)
  504. self.assertEqual(proc3.autostart, True)
  505. self.assertEqual(proc3.autorestart, datatypes.RestartUnconditionally)
  506. self.assertEqual(proc3.uid, None)
  507. self.assertEqual(proc3.stdout_logfile, datatypes.Automatic)
  508. self.assertEqual(proc3.stdout_logfile_maxbytes,
  509. datatypes.byte_size('50MB'))
  510. self.assertEqual(proc3.stdout_logfile_backups, 10)
  511. self.assertEqual(proc3.exitcodes, [0,1,127])
  512. self.assertEqual(proc3.stopsignal, signal.SIGTERM)
  513. self.assertEqual(proc3.stopasgroup, True)
  514. self.assertEqual(proc3.killasgroup, True)
  515. cat4 = options.process_group_configs[3]
  516. self.assertEqual(cat4.name, 'cat4')
  517. self.assertEqual(cat4.priority, 4)
  518. self.assertEqual(len(cat4.process_configs), 2)
  519. proc4_a = cat4.process_configs[0]
  520. self.assertEqual(proc4_a.name, 'fleeb_0')
  521. self.assertEqual(proc4_a.command, '/bin/cat')
  522. self.assertEqual(proc4_a.priority, 4)
  523. self.assertEqual(proc4_a.autostart, True)
  524. self.assertEqual(proc4_a.autorestart,
  525. datatypes.RestartWhenExitUnexpected)
  526. self.assertEqual(proc4_a.uid, None)
  527. self.assertEqual(proc4_a.stdout_logfile, datatypes.Automatic)
  528. self.assertEqual(proc4_a.stdout_logfile_maxbytes,
  529. datatypes.byte_size('50MB'))
  530. self.assertEqual(proc4_a.stdout_logfile_backups, 10)
  531. self.assertEqual(proc4_a.exitcodes, [0,2])
  532. self.assertEqual(proc4_a.stopsignal, signal.SIGTERM)
  533. self.assertEqual(proc4_a.stopasgroup, False)
  534. self.assertEqual(proc4_a.killasgroup, False)
  535. self.assertEqual(proc4_a.directory, None)
  536. proc4_b = cat4.process_configs[1]
  537. self.assertEqual(proc4_b.name, 'fleeb_1')
  538. self.assertEqual(proc4_b.command, '/bin/cat')
  539. self.assertEqual(proc4_b.priority, 4)
  540. self.assertEqual(proc4_b.autostart, True)
  541. self.assertEqual(proc4_b.autorestart,
  542. datatypes.RestartWhenExitUnexpected)
  543. self.assertEqual(proc4_b.uid, None)
  544. self.assertEqual(proc4_b.stdout_logfile, datatypes.Automatic)
  545. self.assertEqual(proc4_b.stdout_logfile_maxbytes,
  546. datatypes.byte_size('50MB'))
  547. self.assertEqual(proc4_b.stdout_logfile_backups, 10)
  548. self.assertEqual(proc4_b.exitcodes, [0,2])
  549. self.assertEqual(proc4_b.stopsignal, signal.SIGTERM)
  550. self.assertEqual(proc4_b.stopasgroup, False)
  551. self.assertEqual(proc4_b.killasgroup, False)
  552. self.assertEqual(proc4_b.directory, None)
  553. cat5 = options.process_group_configs[4]
  554. self.assertEqual(cat5.name, 'cat5')
  555. self.assertEqual(cat5.priority, 5)
  556. self.assertEqual(len(cat5.process_configs), 2)
  557. proc5_a = cat5.process_configs[0]
  558. self.assertEqual(proc5_a.name, 'foo_01')
  559. self.assertEqual(proc5_a.directory, '/some/path/foo_01')
  560. proc5_b = cat5.process_configs[1]
  561. self.assertEqual(proc5_b.name, 'foo_02')
  562. self.assertEqual(proc5_b.directory, '/some/path/foo_02')
  563. here = os.path.abspath(os.getcwd())
  564. self.assertEqual(instance.uid, 0)
  565. self.assertEqual(instance.gid, 0)
  566. self.assertEqual(instance.directory, tempfile.gettempdir())
  567. self.assertEqual(instance.umask, 18) # 022 in Py2, 0o22 in Py3
  568. self.assertEqual(instance.logfile, os.path.join(here,'supervisord.log'))
  569. self.assertEqual(instance.logfile_maxbytes, 1000 * 1024 * 1024)
  570. self.assertEqual(instance.logfile_backups, 5)
  571. self.assertEqual(instance.loglevel, 40)
  572. self.assertEqual(instance.pidfile, os.path.join(here,'supervisord.pid'))
  573. self.assertEqual(instance.nodaemon, True)
  574. self.assertEqual(instance.passwdfile, None)
  575. self.assertEqual(instance.identifier, 'fleeb')
  576. self.assertEqual(instance.childlogdir, tempfile.gettempdir())
  577. self.assertEqual(len(instance.server_configs), 1)
  578. self.assertEqual(instance.server_configs[0]['family'], socket.AF_INET)
  579. self.assertEqual(instance.server_configs[0]['host'], '127.0.0.1')
  580. self.assertEqual(instance.server_configs[0]['port'], 8999)
  581. self.assertEqual(instance.server_configs[0]['username'], 'chrism')
  582. self.assertEqual(instance.server_configs[0]['password'], 'foo')
  583. self.assertEqual(instance.nocleanup, True)
  584. self.assertEqual(instance.minfds, 2048)
  585. self.assertEqual(instance.minprocs, 300)
  586. def test_no_config_file_exits(self):
  587. instance = self._makeOne()
  588. # No default config file search in case they would exist
  589. self.assertTrue(len(instance.searchpaths) > 0)
  590. instance.searchpaths = []
  591. instance.exit = dummy_exit()
  592. # Making sure we capture stdout and stderr
  593. instance.stderr = StringIO()
  594. try:
  595. instance.realize(args=[])
  596. except DummyExitException as e:
  597. # Caught expected exception
  598. import traceback
  599. self.assertEqual(
  600. e.exitcode, 2,
  601. "Wrong exitcode for: %s" % traceback.format_exc()
  602. )
  603. else:
  604. self.fail("Did not get a DummyExitException.")
  605. def test_reload(self):
  606. text = lstrip("""\
  607. [supervisord]
  608. user=root
  609. [program:one]
  610. command = /bin/cat
  611. [program:two]
  612. command = /bin/dog
  613. [program:four]
  614. command = /bin/sheep
  615. [group:thegroup]
  616. programs = one,two
  617. """)
  618. instance = self._makeOne()
  619. instance.configfile = StringIO(text)
  620. instance.realize(args=[])
  621. section = instance.configroot.supervisord
  622. self.assertEqual(len(section.process_group_configs), 2)
  623. cat = section.process_group_configs[0]
  624. self.assertEqual(len(cat.process_configs), 1)
  625. cat = section.process_group_configs[1]
  626. self.assertEqual(len(cat.process_configs), 2)
  627. self.assertTrue(section.process_group_configs is
  628. instance.process_group_configs)
  629. text = lstrip("""\
  630. [supervisord]
  631. user=root
  632. [program:one]
  633. command = /bin/cat
  634. [program:three]
  635. command = /bin/pig
  636. [group:thegroup]
  637. programs = three
  638. """)
  639. instance.configfile = StringIO(text)
  640. instance.process_config()
  641. section = instance.configroot.supervisord
  642. self.assertEqual(len(section.process_group_configs), 2)
  643. cat = section.process_group_configs[0]
  644. self.assertEqual(len(cat.process_configs), 1)
  645. proc = cat.process_configs[0]
  646. self.assertEqual(proc.name, 'one')
  647. self.assertEqual(proc.command, '/bin/cat')
  648. self.assertTrue(section.process_group_configs is
  649. instance.process_group_configs)
  650. cat = section.process_group_configs[1]
  651. self.assertEqual(len(cat.process_configs), 1)
  652. proc = cat.process_configs[0]
  653. self.assertEqual(proc.name, 'three')
  654. self.assertEqual(proc.command, '/bin/pig')
  655. def test_reload_clears_parse_warnings(self):
  656. instance = self._makeOne()
  657. old_warning = "Warning from a prior config read"
  658. instance.parse_warnings = [old_warning]
  659. text = lstrip("""\
  660. [supervisord]
  661. user=root
  662. [program:cat]
  663. command = /bin/cat
  664. """)
  665. instance.configfile = StringIO(text)
  666. instance.realize(args=[])
  667. self.assertFalse(old_warning in instance.parse_warnings)
  668. def test_read_config_not_found(self):
  669. instance = self._makeOne()
  670. def dummy_exists(fn):
  671. return False
  672. instance.exists = dummy_exists
  673. try:
  674. instance.read_config('filename')
  675. except ValueError as e:
  676. self.assertTrue("could not find config file" in str(e))
  677. else:
  678. self.fail("expected exception")
  679. def test_read_config_unreadable(self):
  680. instance = self._makeOne()
  681. def dummy_exists(fn):
  682. return True
  683. instance.exists = dummy_exists
  684. def dummy_open(fn, mode):
  685. raise IOError(errno.EACCES, 'Permission denied: %s' % fn)
  686. instance.open = dummy_open
  687. try:
  688. instance.read_config('filename')
  689. except ValueError as e:
  690. self.assertTrue("could not read config file" in str(e))
  691. else:
  692. self.fail("expected exception")
  693. def test_readFile_failed(self):
  694. from supervisor.options import readFile
  695. try:
  696. readFile('/notthere', 0, 10)
  697. except ValueError as inst:
  698. self.assertEqual(inst.args[0], 'FAILED')
  699. else:
  700. raise AssertionError("Didn't raise")
  701. def test_include_with_no_matching_files_logs_warning(self):
  702. instance = self._makeOne()
  703. text = lstrip("""\
  704. [supervisord]
  705. user=root
  706. [include]
  707. files=nonexistant/*
  708. """)
  709. instance.configfile = StringIO(text)
  710. instance.realize(args=[])
  711. self.assertEqual(instance.parse_warnings,
  712. ['No file matches via include "./nonexistant/*"'])
  713. def test_get_pid(self):
  714. instance = self._makeOne()
  715. self.assertEqual(os.getpid(), instance.get_pid())
  716. def test_get_signal_delegates_to_signal_receiver(self):
  717. instance = self._makeOne()
  718. instance.signal_receiver.receive(signal.SIGTERM, None)
  719. instance.signal_receiver.receive(signal.SIGCHLD, None)
  720. self.assertEqual(instance.get_signal(), signal.SIGTERM)
  721. self.assertEqual(instance.get_signal(), signal.SIGCHLD)
  722. self.assertEqual(instance.get_signal(), None)
  723. def test_check_execv_args_cant_find_command(self):
  724. instance = self._makeOne()
  725. from supervisor.options import NotFound
  726. self.assertRaises(NotFound, instance.check_execv_args,
  727. '/not/there', None, None)
  728. def test_check_execv_args_notexecutable(self):
  729. instance = self._makeOne()
  730. from supervisor.options import NotExecutable
  731. self.assertRaises(NotExecutable,
  732. instance.check_execv_args, '/etc/passwd',
  733. ['etc/passwd'], os.stat('/etc/passwd'))
  734. def test_check_execv_args_isdir(self):
  735. instance = self._makeOne()
  736. from supervisor.options import NotExecutable
  737. self.assertRaises(NotExecutable,
  738. instance.check_execv_args, '/',
  739. ['/'], os.stat('/'))
  740. def test_options_afunix(self):
  741. instance = self._makeOne()
  742. text = lstrip("""\
  743. [unix_http_server]
  744. file=/tmp/supvtest.sock
  745. username=johndoe
  746. password=passwordhere
  747. [supervisord]
  748. ; ...
  749. """)
  750. from supervisor.options import UnhosedConfigParser
  751. config = UnhosedConfigParser()
  752. config.read_string(text)
  753. instance.configfile = StringIO(text)
  754. instance.read_config(StringIO(text))
  755. instance.realize(args=[])
  756. # unix_http_server
  757. options = instance.configroot.supervisord
  758. self.assertEqual(options.server_configs[0]['family'], socket.AF_UNIX)
  759. self.assertEqual(options.server_configs[0]['file'], '/tmp/supvtest.sock')
  760. self.assertEqual(options.server_configs[0]['chmod'], 448) # defaults
  761. self.assertEqual(options.server_configs[0]['chown'], (-1,-1)) # defaults
  762. def test_options_afunix_chxxx_values_valid(self):
  763. instance = self._makeOne()
  764. text = lstrip("""\
  765. [unix_http_server]
  766. file=/tmp/supvtest.sock
  767. username=johndoe
  768. password=passwordhere
  769. chmod=0755
  770. [supervisord]
  771. ; ...
  772. """)
  773. from supervisor.options import UnhosedConfigParser
  774. config = UnhosedConfigParser()
  775. config.read_string(text)
  776. instance.configfile = StringIO(text)
  777. instance.read_config(StringIO(text))
  778. instance.realize(args=[])
  779. # unix_http_server
  780. options = instance.configroot.supervisord
  781. self.assertEqual(options.server_configs[0]['family'], socket.AF_UNIX)
  782. self.assertEqual(options.server_configs[0]['file'], '/tmp/supvtest.sock')
  783. self.assertEqual(options.server_configs[0]['chmod'], 493)
  784. def test_cleanup_afunix_unlink(self):
  785. fn = tempfile.mktemp()
  786. with open(fn, 'w') as f:
  787. f.write('foo')
  788. instance = self._makeOne()
  789. class Server:
  790. pass
  791. instance.httpservers = [({'family':socket.AF_UNIX, 'file':fn},
  792. Server())]
  793. instance.pidfile = ''
  794. instance.cleanup()
  795. self.assertFalse(os.path.exists(fn))
  796. def test_cleanup_afunix_nounlink(self):
  797. fn = tempfile.mktemp()
  798. try:
  799. with open(fn, 'w') as f:
  800. f.write('foo')
  801. instance = self._makeOne()
  802. class Server:
  803. pass
  804. instance.httpservers = [({'family':socket.AF_UNIX, 'file':fn},
  805. Server())]
  806. instance.pidfile = ''
  807. instance.unlink_socketfiles = False
  808. instance.cleanup()
  809. self.assertTrue(os.path.exists(fn))
  810. finally:
  811. try:
  812. os.unlink(fn)
  813. except OSError:
  814. pass
  815. def test_cleanup_afunix_ignores_oserror_enoent(self):
  816. notfound = os.path.join(os.path.dirname(__file__), 'notfound')
  817. socketname = tempfile.mktemp()
  818. try:
  819. with open(socketname, 'w') as f:
  820. f.write('foo')
  821. instance = self._makeOne()
  822. class Server:
  823. pass
  824. instance.httpservers = [
  825. ({'family': socket.AF_UNIX, 'file': notfound}, Server()),
  826. ({'family': socket.AF_UNIX, 'file': socketname}, Server()),
  827. ]
  828. instance.pidfile = ''
  829. instance.cleanup()
  830. self.assertFalse(os.path.exists(socketname))
  831. finally:
  832. try:
  833. os.unlink(socketname)
  834. except OSError:
  835. pass
  836. def test_cleanup_removes_pidfile(self):
  837. pidfile = tempfile.mktemp()
  838. try:
  839. with open(pidfile, 'w') as f:
  840. f.write('2')
  841. instance = self._makeOne()
  842. instance.pidfile = pidfile
  843. instance.cleanup()
  844. self.assertFalse(os.path.exists(pidfile))
  845. finally:
  846. try:
  847. os.unlink(pidfile)
  848. except OSError:
  849. pass
  850. def test_cleanup_pidfile_ignores_oserror_enoent(self):
  851. notfound = os.path.join(os.path.dirname(__file__), 'notfound')
  852. instance = self._makeOne()
  853. instance.pidfile = notfound
  854. instance.cleanup() # shouldn't raise
  855. def test_close_httpservers(self):
  856. instance = self._makeOne()
  857. class Server:
  858. closed = False
  859. def close(self):
  860. self.closed = True
  861. server = Server()
  862. instance.httpservers = [({}, server)]
  863. instance.close_httpservers()
  864. self.assertEqual(server.closed, True)
  865. def test_close_logger(self):
  866. instance = self._makeOne()
  867. logger = DummyLogger()
  868. instance.logger = logger
  869. instance.close_logger()
  870. self.assertEqual(logger.closed, True)
  871. def test_write_pidfile_ok(self):
  872. fn = tempfile.mktemp()
  873. try:
  874. instance = self._makeOne()
  875. instance.logger = DummyLogger()
  876. instance.pidfile = fn
  877. instance.write_pidfile()
  878. self.assertTrue(os.path.exists(fn))
  879. with open(fn, 'r') as f:
  880. pid = int(f.read().strip())
  881. self.assertEqual(pid, os.getpid())
  882. msg = instance.logger.data[0]
  883. self.assertTrue(msg.startswith('supervisord started with pid'))
  884. finally:
  885. try:
  886. os.unlink(fn)
  887. except OSError:
  888. pass
  889. def test_write_pidfile_fail(self):
  890. fn = '/cannot/possibly/exist'
  891. instance = self._makeOne()
  892. instance.logger = DummyLogger()
  893. instance.pidfile = fn
  894. instance.write_pidfile()
  895. msg = instance.logger.data[0]
  896. self.assertTrue(msg.startswith('could not write pidfile'))
  897. def test_close_fd(self):
  898. instance = self._makeOne()
  899. innie, outie = os.pipe()
  900. os.read(innie, 0) # we can read it while its open
  901. os.write(outie, as_bytes('foo')) # we can write to it while its open
  902. instance.close_fd(innie)
  903. self.assertRaises(OSError, os.read, innie, 0)
  904. instance.close_fd(outie)
  905. self.assertRaises(OSError, os.write, outie, as_bytes('foo'))
  906. def test_processes_from_section(self):
  907. instance = self._makeOne()
  908. text = lstrip("""\
  909. [program:foo]
  910. command = /bin/cat
  911. priority = 1
  912. autostart = false
  913. autorestart = false
  914. startsecs = 100
  915. startretries = 100
  916. user = root
  917. stdout_logfile = NONE
  918. stdout_logfile_backups = 1
  919. stdout_logfile_maxbytes = 100MB
  920. stdout_events_enabled = true
  921. stopsignal = KILL
  922. stopwaitsecs = 100
  923. killasgroup = true
  924. exitcodes = 1,4
  925. redirect_stderr = false
  926. environment = KEY1=val1,KEY2=val2,KEY3=%(process_num)s
  927. numprocs = 2
  928. process_name = %(group_name)s_%(program_name)s_%(process_num)02d
  929. """)
  930. from supervisor.options import UnhosedConfigParser
  931. config = UnhosedConfigParser()
  932. config.read_string(text)
  933. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  934. self.assertEqual(len(pconfigs), 2)
  935. pconfig = pconfigs[0]
  936. self.assertEqual(pconfig.name, 'bar_foo_00')
  937. self.assertEqual(pconfig.command, '/bin/cat')
  938. self.assertEqual(pconfig.autostart, False)
  939. self.assertEqual(pconfig.autorestart, False)
  940. self.assertEqual(pconfig.startsecs, 100)
  941. self.assertEqual(pconfig.startretries, 100)
  942. self.assertEqual(pconfig.uid, 0)
  943. self.assertEqual(pconfig.stdout_logfile, None)
  944. self.assertEqual(pconfig.stdout_capture_maxbytes, 0)
  945. self.assertEqual(pconfig.stdout_logfile_maxbytes, 104857600)
  946. self.assertEqual(pconfig.stdout_events_enabled, True)
  947. self.assertEqual(pconfig.stopsignal, signal.SIGKILL)
  948. self.assertEqual(pconfig.stopasgroup, False)
  949. self.assertEqual(pconfig.killasgroup, True)
  950. self.assertEqual(pconfig.stopwaitsecs, 100)
  951. self.assertEqual(pconfig.exitcodes, [1,4])
  952. self.assertEqual(pconfig.redirect_stderr, False)
  953. self.assertEqual(pconfig.environment,
  954. {'KEY1':'val1', 'KEY2':'val2', 'KEY3':'0'})
  955. def test_processes_from_section_host_node_name_expansion(self):
  956. instance = self._makeOne()
  957. text = lstrip("""\
  958. [program:foo]
  959. command = /bin/foo --host=%(host_node_name)s
  960. """)
  961. from supervisor.options import UnhosedConfigParser
  962. config = UnhosedConfigParser()
  963. config.read_string(text)
  964. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  965. import platform
  966. expected = "/bin/foo --host=" + platform.node()
  967. self.assertEqual(pconfigs[0].command, expected)
  968. def test_processes_from_section_process_num_expansion(self):
  969. instance = self._makeOne()
  970. text = lstrip("""\
  971. [program:foo]
  972. command = /bin/foo --num=%(process_num)d
  973. directory = /tmp/foo_%(process_num)d
  974. stderr_logfile = /tmp/foo_%(process_num)d_stderr
  975. stdout_logfile = /tmp/foo_%(process_num)d_stdout
  976. environment = NUM=%(process_num)d
  977. process_name = foo_%(process_num)d
  978. numprocs = 2
  979. """)
  980. from supervisor.options import UnhosedConfigParser
  981. config = UnhosedConfigParser()
  982. config.read_string(text)
  983. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  984. self.assertEqual(len(pconfigs), 2)
  985. for num in (0, 1):
  986. self.assertEqual(pconfigs[num].name, 'foo_%d' % num)
  987. self.assertEqual(pconfigs[num].command, "/bin/foo --num=%d" % num)
  988. self.assertEqual(pconfigs[num].directory, '/tmp/foo_%d' % num)
  989. self.assertEqual(pconfigs[num].stderr_logfile,
  990. '/tmp/foo_%d_stderr' % num)
  991. self.assertEqual(pconfigs[num].stdout_logfile,
  992. '/tmp/foo_%d_stdout' % num)
  993. self.assertEqual(pconfigs[num].environment, {'NUM': '%d' % num})
  994. def test_processes_from_section_expands_directory(self):
  995. instance = self._makeOne()
  996. text = lstrip("""\
  997. [program:foo]
  998. command = /bin/cat
  999. directory = /tmp/%(ENV_FOO)s
  1000. """)
  1001. from supervisor.options import UnhosedConfigParser
  1002. config = UnhosedConfigParser()
  1003. config.expansions = {'ENV_FOO': 'bar'}
  1004. config.read_string(text)
  1005. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  1006. self.assertEqual(pconfigs[0].directory, '/tmp/bar')
  1007. def test_processes_from_section_environment_variables_expansion(self):
  1008. instance = self._makeOne()
  1009. text = lstrip("""\
  1010. [program:foo]
  1011. command = /bin/foo --path='%(ENV_PATH)s'
  1012. """)
  1013. from supervisor.options import UnhosedConfigParser
  1014. config = UnhosedConfigParser()
  1015. config.read_string(text)
  1016. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  1017. expected = "/bin/foo --path='%s'" % os.environ['PATH']
  1018. self.assertEqual(pconfigs[0].command, expected)
  1019. def test_options_with_environment_expansions(self):
  1020. text = lstrip("""\
  1021. [inet_http_server]
  1022. port=*:%(ENV_HTSRV_PORT)s
  1023. username=%(ENV_HTSRV_USER)s
  1024. password=%(ENV_HTSRV_PASS)s
  1025. [supervisord]
  1026. logfile = %(ENV_HOME)s/supervisord.log
  1027. logfile_maxbytes = %(ENV_SUPD_LOGFILE_MAXBYTES)s
  1028. logfile_backups = %(ENV_SUPD_LOGFILE_BACKUPS)s
  1029. loglevel = %(ENV_SUPD_LOGLEVEL)s
  1030. nodaemon = %(ENV_SUPD_NODAEMON)s
  1031. minfds = %(ENV_SUPD_MINFDS)s
  1032. minprocs = %(ENV_SUPD_MINPROCS)s
  1033. umask = %(ENV_SUPD_UMASK)s
  1034. identifier = supervisor_%(ENV_USER)s
  1035. nocleanup = %(ENV_SUPD_NOCLEANUP)s
  1036. childlogdir = %(ENV_HOME)s
  1037. strip_ansi = %(ENV_SUPD_STRIP_ANSI)s
  1038. environment = FAKE_ENV_VAR=/some/path
  1039. [program:cat1]
  1040. command=%(ENV_CAT1_COMMAND)s --logdir=%(ENV_CAT1_COMMAND_LOGDIR)s
  1041. priority=%(ENV_CAT1_PRIORITY)s
  1042. autostart=%(ENV_CAT1_AUTOSTART)s
  1043. user=%(ENV_CAT1_USER)s
  1044. stdout_logfile=%(ENV_CAT1_STDOUT_LOGFILE)s
  1045. stdout_logfile_maxbytes = %(ENV_CAT1_STDOUT_LOGFILE_MAXBYTES)s
  1046. stdout_logfile_backups = %(ENV_CAT1_STDOUT_LOGFILE_BACKUPS)s
  1047. stopsignal=%(ENV_CAT1_STOPSIGNAL)s
  1048. stopwaitsecs=%(ENV_CAT1_STOPWAIT)s
  1049. startsecs=%(ENV_CAT1_STARTWAIT)s
  1050. startretries=%(ENV_CAT1_STARTRETRIES)s
  1051. directory=%(ENV_CAT1_DIR)s
  1052. umask=%(ENV_CAT1_UMASK)s
  1053. """)
  1054. from supervisor import datatypes
  1055. from supervisor.options import UnhosedConfigParser
  1056. instance = self._makeOne()
  1057. instance.environ_expansions = {
  1058. 'ENV_HOME': tempfile.gettempdir(),
  1059. 'ENV_USER': 'johndoe',
  1060. 'ENV_HTSRV_PORT': '9210',
  1061. 'ENV_HTSRV_USER': 'someuser',
  1062. 'ENV_HTSRV_PASS': 'passwordhere',
  1063. 'ENV_SUPD_LOGFILE_MAXBYTES': '51MB',
  1064. 'ENV_SUPD_LOGFILE_BACKUPS': '10',
  1065. 'ENV_SUPD_LOGLEVEL': 'info',
  1066. 'ENV_SUPD_NODAEMON': 'false',
  1067. 'ENV_SUPD_MINFDS': '1024',
  1068. 'ENV_SUPD_MINPROCS': '200',
  1069. 'ENV_SUPD_UMASK': '002',
  1070. 'ENV_SUPD_NOCLEANUP': 'true',
  1071. 'ENV_SUPD_STRIP_ANSI': 'false',
  1072. 'ENV_CAT1_COMMAND': '/bin/customcat',
  1073. 'ENV_CAT1_COMMAND_LOGDIR': '/path/to/logs',
  1074. 'ENV_CAT1_PRIORITY': '3',
  1075. 'ENV_CAT1_AUTOSTART': 'true',
  1076. 'ENV_CAT1_USER': 'root', # resolved to uid
  1077. 'ENV_CAT1_STDOUT_LOGFILE': '/tmp/cat.log',
  1078. 'ENV_CAT1_STDOUT_LOGFILE_MAXBYTES': '78KB',
  1079. 'ENV_CAT1_STDOUT_LOGFILE_BACKUPS': '2',
  1080. 'ENV_CAT1_STOPSIGNAL': 'KILL',
  1081. 'ENV_CAT1_STOPWAIT': '5',
  1082. 'ENV_CAT1_STARTWAIT': '5',
  1083. 'ENV_CAT1_STARTRETRIES': '10',
  1084. 'ENV_CAT1_DIR': '/tmp',
  1085. 'ENV_CAT1_UMASK': '002',
  1086. }
  1087. config = UnhosedConfigParser()
  1088. config.expansions = instance.environ_expansions
  1089. config.read_string(text)
  1090. instance.configfile = StringIO(text)
  1091. instance.read_config(StringIO(text))
  1092. instance.realize(args=[])
  1093. # supervisord
  1094. self.assertEqual(instance.logfile,
  1095. '%(ENV_HOME)s/supervisord.log' % config.expansions)
  1096. self.assertEqual(instance.identifier,
  1097. 'supervisor_%(ENV_USER)s' % config.expansions)
  1098. self.assertEqual(instance.logfile_maxbytes, 53477376)
  1099. self.assertEqual(instance.logfile_backups, 10)
  1100. self.assertEqual(instance.loglevel, LevelsByName.INFO)
  1101. self.assertEqual(instance.nodaemon, False)
  1102. self.assertEqual(instance.minfds, 1024)
  1103. self.assertEqual(instance.minprocs, 200)
  1104. self.assertEqual(instance.nocleanup, True)
  1105. self.assertEqual(instance.childlogdir, config.expansions['ENV_HOME'])
  1106. self.assertEqual(instance.strip_ansi, False)
  1107. # inet_http_server
  1108. options = instance.configroot.supervisord
  1109. self.assertEqual(options.server_configs[0]['family'], socket.AF_INET)
  1110. self.assertEqual(options.server_configs[0]['host'], '')
  1111. self.assertEqual(options.server_configs[0]['port'], 9210)
  1112. self.assertEqual(options.server_configs[0]['username'], 'someuser')
  1113. self.assertEqual(options.server_configs[0]['password'], 'passwordhere')
  1114. # cat1
  1115. cat1 = options.process_group_configs[0]
  1116. self.assertEqual(cat1.name, 'cat1')
  1117. self.assertEqual(cat1.priority, 3)
  1118. self.assertEqual(len(cat1.process_configs), 1)
  1119. proc1 = cat1.process_configs[0]
  1120. self.assertEqual(proc1.name, 'cat1')
  1121. self.assertEqual(proc1.command,
  1122. '/bin/customcat --logdir=/path/to/logs')
  1123. self.assertEqual(proc1.priority, 3)
  1124. self.assertEqual(proc1.autostart, True)
  1125. self.assertEqual(proc1.autorestart, datatypes.RestartWhenExitUnexpected)
  1126. self.assertEqual(proc1.startsecs, 5)
  1127. self.assertEqual(proc1.startretries, 10)
  1128. self.assertEqual(proc1.uid, 0)
  1129. self.assertEqual(proc1.stdout_logfile, '/tmp/cat.log')
  1130. self.assertEqual(proc1.stopsignal, signal.SIGKILL)
  1131. self.assertEqual(proc1.stopwaitsecs, 5)
  1132. self.assertEqual(proc1.stopasgroup, False)
  1133. self.assertEqual(proc1.killasgroup, False)
  1134. self.assertEqual(proc1.stdout_logfile_maxbytes,
  1135. datatypes.byte_size('78KB'))
  1136. self.assertEqual(proc1.stdout_logfile_backups, 2)
  1137. self.assertEqual(proc1.exitcodes, [0,2])
  1138. self.assertEqual(proc1.directory, '/tmp')
  1139. self.assertEqual(proc1.umask, 2)
  1140. self.assertEqual(proc1.environment, dict(FAKE_ENV_VAR='/some/path'))
  1141. def test_processes_from_section_bad_program_name_spaces(self):
  1142. instance = self._makeOne()
  1143. text = lstrip("""\
  1144. [program:spaces are bad]
  1145. """)
  1146. from supervisor.options import UnhosedConfigParser
  1147. config = UnhosedConfigParser()
  1148. config.read_string(text)
  1149. self.assertRaises(ValueError, instance.processes_from_section,
  1150. config, 'program:spaces are bad', None)
  1151. def test_processes_from_section_bad_program_name_colons(self):
  1152. instance = self._makeOne()
  1153. text = lstrip("""\
  1154. [program:colons:are:bad]
  1155. """)
  1156. from supervisor.options import UnhosedConfigParser
  1157. config = UnhosedConfigParser()
  1158. config.read_string(text)
  1159. self.assertRaises(ValueError, instance.processes_from_section,
  1160. config, 'program:colons:are:bad', None)
  1161. def test_processes_from_section_no_procnum_in_processname(self):
  1162. instance = self._makeOne()
  1163. text = lstrip("""\
  1164. [program:foo]
  1165. command = /bin/cat
  1166. numprocs = 2
  1167. """)
  1168. from supervisor.options import UnhosedConfigParser
  1169. config = UnhosedConfigParser()
  1170. config.read_string(text)
  1171. self.assertRaises(ValueError, instance.processes_from_section,
  1172. config, 'program:foo', None)
  1173. def test_processes_from_section_no_command(self):
  1174. instance = self._makeOne()
  1175. text = lstrip("""\
  1176. [program:foo]
  1177. """)
  1178. from supervisor.options import UnhosedConfigParser
  1179. config = UnhosedConfigParser()
  1180. config.read_string(text)
  1181. try:
  1182. instance.processes_from_section(config, 'program:foo', None)
  1183. self.fail('nothing raised')
  1184. except ValueError as exc:
  1185. self.assertTrue(exc.args[0].startswith(
  1186. 'program section program:foo does not specify a command'))
  1187. def test_processes_from_section_missing_replacement_in_process_name(self):
  1188. instance = self._makeOne()
  1189. text = lstrip("""\
  1190. [program:foo]
  1191. command = /bin/cat
  1192. process_name = %(not_there)s
  1193. """)
  1194. from supervisor.options import UnhosedConfigParser
  1195. config = UnhosedConfigParser()
  1196. config.read_string(text)
  1197. self.assertRaises(ValueError, instance.processes_from_section,
  1198. config, 'program:foo', None)
  1199. def test_processes_from_section_bad_expression_in_process_name(self):
  1200. instance = self._makeOne()
  1201. text = lstrip("""\
  1202. [program:foo]
  1203. command = /bin/cat
  1204. process_name = %(program_name)
  1205. """)
  1206. from supervisor.options import UnhosedConfigParser
  1207. config = UnhosedConfigParser()
  1208. config.read_string(text)
  1209. self.assertRaises(ValueError, instance.processes_from_section,
  1210. config, 'program:foo', None)
  1211. def test_processes_from_section_bad_chars_in_process_name(self):
  1212. instance = self._makeOne()
  1213. text = lstrip("""\
  1214. [program:foo]
  1215. command = /bin/cat
  1216. process_name = colons:are:bad
  1217. """)
  1218. from supervisor.options import UnhosedConfigParser
  1219. config = UnhosedConfigParser()
  1220. config.read_string(text)
  1221. self.assertRaises(ValueError, instance.processes_from_section,
  1222. config, 'program:foo', None)
  1223. def test_processes_from_section_stopasgroup_implies_killasgroup(self):
  1224. instance = self._makeOne()
  1225. text = lstrip("""\
  1226. [program:foo]
  1227. command = /bin/cat
  1228. process_name = %(program_name)s
  1229. stopasgroup = true
  1230. """)
  1231. from supervisor.options import UnhosedConfigParser
  1232. config = UnhosedConfigParser()
  1233. config.read_string(text)
  1234. pconfigs = instance.processes_from_section(config, 'program:foo', 'bar')
  1235. self.assertEqual(len(pconfigs), 1)
  1236. pconfig = pconfigs[0]
  1237. self.assertEqual(pconfig.stopasgroup, True)
  1238. self.assertEqual(pconfig.killasgroup, True)
  1239. def test_processes_from_section_killasgroup_mismatch_w_stopasgroup(self):
  1240. instance = self._makeOne()
  1241. text = lstrip("""\
  1242. [program:foo]
  1243. command = /bin/cat
  1244. process_name = %(program_name)s
  1245. stopasgroup = true
  1246. killasgroup = false
  1247. """)
  1248. from supervisor.options import UnhosedConfigParser
  1249. config = UnhosedConfigParser()
  1250. config.read_string(text)
  1251. self.assertRaises(ValueError, instance.processes_from_section,
  1252. config, 'program:foo', None)
  1253. def test_processes_from_section_unexpected_end_of_key_value_pairs(self):
  1254. instance = self._makeOne()
  1255. text = lstrip("""\
  1256. [program:foo]
  1257. command = /bin/cat
  1258. environment = KEY1=val1,KEY2=val2,KEY3
  1259. """)
  1260. from supervisor.options import UnhosedConfigParser
  1261. config = UnhosedConfigParser()
  1262. config.read_string(text)
  1263. try:
  1264. instance.processes_from_section(config, 'program:foo', None)
  1265. except ValueError as e:
  1266. self.assertTrue(
  1267. "Unexpected end of key/value pairs in value "
  1268. "'KEY1=val1,KEY2=val2,KEY3' in section 'program:foo'"
  1269. in str(e))
  1270. else:
  1271. self.fail('instance.processes_from_section should '
  1272. 'raise a ValueError')
  1273. def test_processes_from_section_shows_conf_filename_on_valueerror(self):
  1274. instance = self._makeOne()
  1275. text = lstrip("""\
  1276. [program:foo]
  1277. ;no command
  1278. """)
  1279. f = tempfile.NamedTemporaryFile(mode="w+")
  1280. try:
  1281. f.write(text)
  1282. f.flush()
  1283. from supervisor.options import UnhosedConfigParser
  1284. config = UnhosedConfigParser()
  1285. config.read(f.name)
  1286. instance.processes_from_section(config, 'program:foo', None)
  1287. except ValueError as e:
  1288. self.assertEqual(e.args[0],
  1289. "program section program:foo does not specify a command "
  1290. "in section 'program:foo' (file: %s)" % f.name)
  1291. else:
  1292. self.fail('nothing raised')
  1293. finally:
  1294. f.close()
  1295. def test_processes_from_autolog_without_rollover(self):
  1296. instance = self._makeOne()
  1297. text = lstrip("""\
  1298. [program:foo]
  1299. command = /bin/foo
  1300. stdout_logfile = AUTO
  1301. stdout_logfile_maxbytes = 0
  1302. stderr_logfile = AUTO
  1303. stderr_logfile_maxbytes = 0
  1304. """)
  1305. from supervisor.options import UnhosedConfigParser
  1306. config = UnhosedConfigParser()
  1307. instance.logger = DummyLogger()
  1308. config.read_string(text)
  1309. instance.processes_from_section(config, 'program:foo', None)
  1310. self.assertEqual(instance.parse_warnings[0],
  1311. 'For [program:foo], AUTO logging used for stdout_logfile '
  1312. 'without rollover, set maxbytes > 0 to avoid filling up '
  1313. 'filesystem unintentionally')
  1314. self.assertEqual(instance.parse_warnings[1],
  1315. 'For [program:foo], AUTO logging used for stderr_logfile '
  1316. 'without rollover, set maxbytes > 0 to avoid filling up '
  1317. 'filesystem unintentionally')
  1318. def test_homogeneous_process_groups_from_parser(self):
  1319. text = lstrip("""\
  1320. [program:many]
  1321. process_name = %(program_name)s_%(process_num)s
  1322. command = /bin/cat
  1323. numprocs = 2
  1324. priority = 1
  1325. """)
  1326. from supervisor.options import UnhosedConfigParser
  1327. config = UnhosedConfigParser()
  1328. config.read_string(text)
  1329. instance = self._makeOne()
  1330. gconfigs = instance.process_groups_from_parser(config)
  1331. self.assertEqual(len(gconfigs), 1)
  1332. gconfig = gconfigs[0]
  1333. self.assertEqual(gconfig.name, 'many')
  1334. self.assertEqual(gconfig.priority, 1)
  1335. self.assertEqual(len(gconfig.process_configs), 2)
  1336. def test_event_listener_pools_from_parser(self):
  1337. text = lstrip("""\
  1338. [eventlistener:dog]
  1339. events=PROCESS_COMMUNICATION
  1340. process_name = %(program_name)s_%(process_num)s
  1341. command = /bin/dog
  1342. numprocs = 2
  1343. priority = 1
  1344. [eventlistener:cat]
  1345. events=PROCESS_COMMUNICATION
  1346. process_name = %(program_name)s_%(process_num)s
  1347. command = /bin/cat
  1348. numprocs = 3
  1349. [eventlistener:biz]
  1350. events=PROCESS_COMMUNICATION
  1351. process_name = %(program_name)s_%(process_num)s
  1352. command = /bin/biz
  1353. numprocs = 2
  1354. """)
  1355. from supervisor.options import UnhosedConfigParser
  1356. from supervisor.dispatchers import default_handler
  1357. config = UnhosedConfigParser()
  1358. config.read_string(text)
  1359. instance = self._makeOne()
  1360. gconfigs = instance.process_groups_from_parser(config)
  1361. self.assertEqual(len(gconfigs), 3)
  1362. gconfig1 = gconfigs[0]
  1363. self.assertEqual(gconfig1.name, 'biz')
  1364. self.assertEqual(gconfig1.result_handler, default_handler)
  1365. self.assertEqual(len(gconfig1.process_configs), 2)
  1366. gconfig1 = gconfigs[1]
  1367. self.assertEqual(gconfig1.name, 'cat')
  1368. self.assertEqual(gconfig1.priority, -1)
  1369. self.assertEqual(gconfig1.result_handler, default_handler)
  1370. self.assertEqual(len(gconfig1.process_configs), 3)
  1371. gconfig1 = gconfigs[2]
  1372. self.assertEqual(gconfig1.name, 'dog')
  1373. self.assertEqual(gconfig1.priority, 1)
  1374. self.assertEqual(gconfig1.result_handler, default_handler)
  1375. self.assertEqual(len(gconfig1.process_configs), 2)
  1376. def test_event_listener_pools_from_parser_with_environment_expansions(self):
  1377. text = lstrip("""\
  1378. [eventlistener:dog]
  1379. events=PROCESS_COMMUNICATION
  1380. process_name = %(ENV_EL1_PROCNAME)s_%(program_name)s_%(process_num)s
  1381. command = %(ENV_EL1_COMMAND)s
  1382. numprocs = %(ENV_EL1_NUMPROCS)s
  1383. priority = %(ENV_EL1_PRIORITY)s
  1384. [eventlistener:cat]
  1385. events=PROCESS_COMMUNICATION
  1386. process_name = %(program_name)s_%(process_num)s
  1387. command = /bin/cat
  1388. numprocs = 3
  1389. """)
  1390. from supervisor.options import UnhosedConfigParser
  1391. from supervisor.dispatchers import default_handler
  1392. instance = self._makeOne()
  1393. instance.environ_expansions = {'ENV_HOME': tempfile.gettempdir(),
  1394. 'ENV_USER': 'johndoe',
  1395. 'ENV_EL1_PROCNAME': 'myeventlistener',
  1396. 'ENV_EL1_COMMAND': '/bin/dog',
  1397. 'ENV_EL1_NUMPROCS': '2',
  1398. 'ENV_EL1_PRIORITY': '1',
  1399. }
  1400. config = UnhosedConfigParser()
  1401. config.expansions = instance.environ_expansions
  1402. config.read_string(text)
  1403. gconfigs = instance.process_groups_from_parser(config)
  1404. self.assertEqual(len(gconfigs), 2)
  1405. gconfig0 = gconfigs[0]
  1406. self.assertEqual(gconfig0.name, 'cat')
  1407. self.assertEqual(gconfig0.priority, -1)
  1408. self.assertEqual(gconfig0.result_handler, default_handler)
  1409. self.assertEqual(len(gconfig0.process_configs), 3)
  1410. gconfig1 = gconfigs[1]
  1411. self.assertEqual(gconfig1.name, 'dog')
  1412. self.assertEqual(gconfig1.priority, 1)
  1413. self.assertEqual(gconfig1.result_handler, default_handler)
  1414. self.assertEqual(len(gconfig1.process_configs), 2)
  1415. dog0 = gconfig1.process_configs[0]
  1416. self.assertEqual(dog0.name, 'myeventlistener_dog_0')
  1417. self.assertEqual(dog0.command, '/bin/dog')
  1418. self.assertEqual(dog0.priority, 1)
  1419. dog1 = gconfig1.process_configs[1]
  1420. self.assertEqual(dog1.name, 'myeventlistener_dog_1')
  1421. self.assertEqual(dog1.command, '/bin/dog')
  1422. self.assertEqual(dog1.priority, 1)
  1423. def test_event_listener_pool_disallows_redirect_stderr(self):
  1424. text = lstrip("""\
  1425. [eventlistener:dog]
  1426. events=PROCESS_COMMUNICATION
  1427. command = /bin/dog
  1428. redirect_stderr = True
  1429. """)
  1430. from supervisor.options import UnhosedConfigParser
  1431. config = UnhosedConfigParser()
  1432. config.read_string(text)
  1433. instance = self._makeOne()
  1434. try:
  1435. instance.process_groups_from_parser(config)
  1436. self.fail('nothing raised')
  1437. except ValueError as exc:
  1438. self.assertEqual(exc.args[0], '[eventlistener:dog] section sets '
  1439. 'redirect_stderr=true but this is not allowed because it '
  1440. 'will interfere with the eventlistener protocol')
  1441. def test_event_listener_pool_with_event_result_handler(self):
  1442. text = lstrip("""\
  1443. [eventlistener:dog]
  1444. events=PROCESS_COMMUNICATION
  1445. command = /bin/dog
  1446. result_handler = supervisor.tests.base:dummy_handler
  1447. """)
  1448. from supervisor.options import UnhosedConfigParser
  1449. from supervisor.tests.base import dummy_handler
  1450. config = UnhosedConfigParser()
  1451. config.read_string(text)
  1452. instance = self._makeOne()
  1453. gconfigs = instance.process_groups_from_parser(config)
  1454. self.assertEqual(len(gconfigs), 1)
  1455. gconfig1 = gconfigs[0]
  1456. self.assertEqual(gconfig1.result_handler, dummy_handler)
  1457. def test_event_listener_pool_result_handler_unimportable(self):
  1458. text = lstrip("""\
  1459. [eventlistener:cat]
  1460. events=PROCESS_COMMUNICATION
  1461. command = /bin/cat
  1462. result_handler = supervisor.tests.base:nonexistant
  1463. """)
  1464. from supervisor.options import UnhosedConfigParser
  1465. config = UnhosedConfigParser()
  1466. config.read_string(text)
  1467. instance = self._makeOne()
  1468. try:
  1469. instance.process_groups_from_parser(config)
  1470. self.fail('nothing raised')
  1471. except ValueError as exc:
  1472. self.assertEqual(exc.args[0],
  1473. 'supervisor.tests.base:nonexistant cannot be '
  1474. 'resolved within [eventlistener:cat]')
  1475. def test_event_listener_pool_noeventsline(self):
  1476. text = lstrip("""\
  1477. [eventlistener:dog]
  1478. process_name = %(program_name)s_%(process_num)s
  1479. command = /bin/dog
  1480. numprocs = 2
  1481. priority = 1
  1482. """)
  1483. from supervisor.options import UnhosedConfigParser
  1484. config = UnhosedConfigParser()
  1485. config.read_string(text)
  1486. instance = self._makeOne()
  1487. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1488. def test_event_listener_pool_unknown_eventtype(self):
  1489. text = lstrip("""\
  1490. [eventlistener:dog]
  1491. events=PROCESS_COMMUNICATION,THIS_EVENT_TYPE_DOESNT_EXIST
  1492. process_name = %(program_name)s_%(process_num)s
  1493. command = /bin/dog
  1494. numprocs = 2
  1495. priority = 1
  1496. """)
  1497. from supervisor.options import UnhosedConfigParser
  1498. config = UnhosedConfigParser()
  1499. config.read_string(text)
  1500. instance = self._makeOne()
  1501. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1502. def test_fcgi_programs_from_parser(self):
  1503. from supervisor.options import FastCGIGroupConfig
  1504. from supervisor.options import FastCGIProcessConfig
  1505. text = lstrip("""\
  1506. [fcgi-program:foo]
  1507. socket = unix:///tmp/%(program_name)s.sock
  1508. socket_owner = testuser:testgroup
  1509. socket_mode = 0666
  1510. process_name = %(program_name)s_%(process_num)s
  1511. command = /bin/foo
  1512. numprocs = 2
  1513. priority = 1
  1514. [fcgi-program:bar]
  1515. socket = unix:///tmp/%(program_name)s.sock
  1516. process_name = %(program_name)s_%(process_num)s
  1517. command = /bin/bar
  1518. user = testuser
  1519. numprocs = 3
  1520. [fcgi-program:flub]
  1521. socket = unix:///tmp/%(program_name)s.sock
  1522. command = /bin/flub
  1523. [fcgi-program:cub]
  1524. socket = tcp://localhost:6000
  1525. command = /bin/cub
  1526. """)
  1527. from supervisor.options import UnhosedConfigParser
  1528. config = UnhosedConfigParser()
  1529. config.read_string(text)
  1530. instance = self._makeOne()
  1531. #Patch pwd and grp module functions to give us sentinel
  1532. #uid/gid values so that the test does not depend on
  1533. #any specific system users
  1534. pwd_mock = Mock()
  1535. pwd_mock.return_value = (None, None, sentinel.uid, sentinel.gid)
  1536. grp_mock = Mock()
  1537. grp_mock.return_value = (None, None, sentinel.gid)
  1538. @patch('pwd.getpwuid', pwd_mock)
  1539. @patch('pwd.getpwnam', pwd_mock)
  1540. @patch('grp.getgrnam', grp_mock)
  1541. def get_process_groups(instance, config):
  1542. return instance.process_groups_from_parser(config)
  1543. gconfigs = get_process_groups(instance, config)
  1544. exp_owner = (sentinel.uid, sentinel.gid)
  1545. self.assertEqual(len(gconfigs), 4)
  1546. gconf_foo = gconfigs[0]
  1547. self.assertEqual(gconf_foo.__class__, FastCGIGroupConfig)
  1548. self.assertEqual(gconf_foo.name, 'foo')
  1549. self.assertEqual(gconf_foo.priority, 1)
  1550. self.assertEqual(gconf_foo.socket_config.url,
  1551. 'unix:///tmp/foo.sock')
  1552. self.assertEqual(exp_owner, gconf_foo.socket_config.get_owner())
  1553. self.assertEqual(438, gconf_foo.socket_config.get_mode()) # 0666 in Py2, 0o666 in Py3
  1554. self.assertEqual(len(gconf_foo.process_configs), 2)
  1555. pconfig_foo = gconf_foo.process_configs[0]
  1556. self.assertEqual(pconfig_foo.__class__, FastCGIProcessConfig)
  1557. gconf_bar = gconfigs[1]
  1558. self.assertEqual(gconf_bar.name, 'bar')
  1559. self.assertEqual(gconf_bar.priority, 999)
  1560. self.assertEqual(gconf_bar.socket_config.url,
  1561. 'unix:///tmp/bar.sock')
  1562. self.assertEqual(exp_owner, gconf_bar.socket_config.get_owner())
  1563. self.assertEqual(448, gconf_bar.socket_config.get_mode()) # 0700 in Py2, 0o700 in Py3
  1564. self.assertEqual(len(gconf_bar.process_configs), 3)
  1565. gconf_cub = gconfigs[2]
  1566. self.assertEqual(gconf_cub.name, 'cub')
  1567. self.assertEqual(gconf_cub.socket_config.url,
  1568. 'tcp://localhost:6000')
  1569. self.assertEqual(len(gconf_cub.process_configs), 1)
  1570. gconf_flub = gconfigs[3]
  1571. self.assertEqual(gconf_flub.name, 'flub')
  1572. self.assertEqual(gconf_flub.socket_config.url,
  1573. 'unix:///tmp/flub.sock')
  1574. self.assertEqual(None, gconf_flub.socket_config.get_owner())
  1575. self.assertEqual(448, gconf_flub.socket_config.get_mode()) # 0700 in Py2, 0o700 in Py3
  1576. self.assertEqual(len(gconf_flub.process_configs), 1)
  1577. def test_fcgi_programs_from_parser_with_environment_expansions(self):
  1578. from supervisor.options import FastCGIGroupConfig
  1579. from supervisor.options import FastCGIProcessConfig
  1580. text = lstrip("""\
  1581. [fcgi-program:foo]
  1582. socket = unix:///tmp/%(program_name)s%(ENV_FOO_SOCKET_EXT)s
  1583. socket_owner = %(ENV_FOO_SOCKET_USER)s:testgroup
  1584. socket_mode = %(ENV_FOO_SOCKET_MODE)s
  1585. process_name = %(ENV_FOO_PROCESS_PREFIX)s_%(program_name)s_%(process_num)s
  1586. command = /bin/foo --arg1=%(ENV_FOO_COMMAND_ARG1)s
  1587. numprocs = %(ENV_FOO_NUMPROCS)s
  1588. priority = %(ENV_FOO_PRIORITY)s
  1589. """)
  1590. from supervisor.options import UnhosedConfigParser
  1591. instance = self._makeOne()
  1592. instance.environ_expansions = {'ENV_HOME': '/tmp',
  1593. 'ENV_SERVER_PORT': '9210',
  1594. 'ENV_FOO_SOCKET_EXT': '.usock',
  1595. 'ENV_FOO_SOCKET_USER': 'testuser',
  1596. 'ENV_FOO_SOCKET_MODE': '0666',
  1597. 'ENV_FOO_PROCESS_PREFIX': 'fcgi-',
  1598. 'ENV_FOO_COMMAND_ARG1': 'bar',
  1599. 'ENV_FOO_NUMPROCS': '2',
  1600. 'ENV_FOO_PRIORITY': '1',
  1601. }
  1602. config = UnhosedConfigParser()
  1603. config.expansions = instance.environ_expansions
  1604. config.read_string(text)
  1605. #Patch pwd and grp module functions to give us sentinel
  1606. #uid/gid values so that the test does not depend on
  1607. #any specific system users
  1608. pwd_mock = Mock()
  1609. pwd_mock.return_value = (None, None, sentinel.uid, sentinel.gid)
  1610. grp_mock = Mock()
  1611. grp_mock.return_value = (None, None, sentinel.gid)
  1612. @patch('pwd.getpwuid', pwd_mock)
  1613. @patch('pwd.getpwnam', pwd_mock)
  1614. @patch('grp.getgrnam', grp_mock)
  1615. def get_process_groups(instance, config):
  1616. return instance.process_groups_from_parser(config)
  1617. gconfigs = get_process_groups(instance, config)
  1618. exp_owner = (sentinel.uid, sentinel.gid)
  1619. self.assertEqual(len(gconfigs), 1)
  1620. gconf_foo = gconfigs[0]
  1621. self.assertEqual(gconf_foo.__class__, FastCGIGroupConfig)
  1622. self.assertEqual(gconf_foo.name, 'foo')
  1623. self.assertEqual(gconf_foo.priority, 1)
  1624. self.assertEqual(gconf_foo.socket_config.url,
  1625. 'unix:///tmp/foo.usock')
  1626. self.assertEqual(exp_owner, gconf_foo.socket_config.get_owner())
  1627. self.assertEqual(438, gconf_foo.socket_config.get_mode()) # 0666 in Py2, 0o666 in Py3
  1628. self.assertEqual(len(gconf_foo.process_configs), 2)
  1629. pconfig_foo = gconf_foo.process_configs[0]
  1630. self.assertEqual(pconfig_foo.__class__, FastCGIProcessConfig)
  1631. self.assertEqual(pconfig_foo.command, '/bin/foo --arg1=bar')
  1632. def test_fcgi_program_no_socket(self):
  1633. text = lstrip("""\
  1634. [fcgi-program:foo]
  1635. process_name = %(program_name)s_%(process_num)s
  1636. command = /bin/foo
  1637. numprocs = 2
  1638. priority = 1
  1639. """)
  1640. from supervisor.options import UnhosedConfigParser
  1641. config = UnhosedConfigParser()
  1642. config.read_string(text)
  1643. instance = self._makeOne()
  1644. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1645. def test_fcgi_program_unknown_socket_protocol(self):
  1646. text = lstrip("""\
  1647. [fcgi-program:foo]
  1648. socket=junk://blah
  1649. process_name = %(program_name)s_%(process_num)s
  1650. command = /bin/foo
  1651. numprocs = 2
  1652. priority = 1
  1653. """)
  1654. from supervisor.options import UnhosedConfigParser
  1655. config = UnhosedConfigParser()
  1656. config.read_string(text)
  1657. instance = self._makeOne()
  1658. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1659. def test_fcgi_program_rel_unix_sock_path(self):
  1660. text = lstrip("""\
  1661. [fcgi-program:foo]
  1662. socket=unix://relative/path
  1663. process_name = %(program_name)s_%(process_num)s
  1664. command = /bin/foo
  1665. numprocs = 2
  1666. priority = 1
  1667. """)
  1668. from supervisor.options import UnhosedConfigParser
  1669. config = UnhosedConfigParser()
  1670. config.read_string(text)
  1671. instance = self._makeOne()
  1672. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1673. def test_fcgi_program_bad_tcp_sock_format(self):
  1674. text = lstrip("""\
  1675. [fcgi-program:foo]
  1676. socket=tcp://missingport
  1677. process_name = %(program_name)s_%(process_num)s
  1678. command = /bin/foo
  1679. numprocs = 2
  1680. priority = 1
  1681. """)
  1682. from supervisor.options import UnhosedConfigParser
  1683. config = UnhosedConfigParser()
  1684. config.read_string(text)
  1685. instance = self._makeOne()
  1686. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1687. def test_fcgi_program_bad_expansion_proc_num(self):
  1688. text = lstrip("""\
  1689. [fcgi-program:foo]
  1690. socket=unix:///tmp/%(process_num)s.sock
  1691. process_name = %(program_name)s_%(process_num)s
  1692. command = /bin/foo
  1693. numprocs = 2
  1694. priority = 1
  1695. """)
  1696. from supervisor.options import UnhosedConfigParser
  1697. config = UnhosedConfigParser()
  1698. config.read_string(text)
  1699. instance = self._makeOne()
  1700. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1701. def test_fcgi_program_socket_owner_set_for_tcp(self):
  1702. text = lstrip("""\
  1703. [fcgi-program:foo]
  1704. socket=tcp://localhost:8000
  1705. socket_owner=nobody:nobody
  1706. command = /bin/foo
  1707. """)
  1708. from supervisor.options import UnhosedConfigParser
  1709. config = UnhosedConfigParser()
  1710. config.read_string(text)
  1711. instance = self._makeOne()
  1712. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1713. def test_fcgi_program_socket_mode_set_for_tcp(self):
  1714. text = lstrip("""\
  1715. [fcgi-program:foo]
  1716. socket = tcp://localhost:8000
  1717. socket_mode = 0777
  1718. command = /bin/foo
  1719. """)
  1720. from supervisor.options import UnhosedConfigParser
  1721. config = UnhosedConfigParser()
  1722. config.read_string(text)
  1723. instance = self._makeOne()
  1724. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1725. def test_fcgi_program_bad_socket_owner(self):
  1726. text = lstrip("""\
  1727. [fcgi-program:foo]
  1728. socket = unix:///tmp/foo.sock
  1729. socket_owner = sometotaljunkuserthatshouldnobethere
  1730. command = /bin/foo
  1731. """)
  1732. from supervisor.options import UnhosedConfigParser
  1733. config = UnhosedConfigParser()
  1734. config.read_string(text)
  1735. instance = self._makeOne()
  1736. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1737. def test_fcgi_program_bad_socket_mode(self):
  1738. text = lstrip("""\
  1739. [fcgi-program:foo]
  1740. socket = unix:///tmp/foo.sock
  1741. socket_mode = junk
  1742. command = /bin/foo
  1743. """)
  1744. from supervisor.options import UnhosedConfigParser
  1745. config = UnhosedConfigParser()
  1746. config.read_string(text)
  1747. instance = self._makeOne()
  1748. self.assertRaises(ValueError,instance.process_groups_from_parser,config)
  1749. def test_heterogeneous_process_groups_from_parser(self):
  1750. text = lstrip("""\
  1751. [program:one]
  1752. command = /bin/cat
  1753. [program:two]
  1754. command = /bin/cat
  1755. [group:thegroup]
  1756. programs = one,two
  1757. priority = 5
  1758. """)
  1759. from supervisor.options import UnhosedConfigParser
  1760. config = UnhosedConfigParser()
  1761. config.read_string(text)
  1762. instance = self._makeOne()
  1763. gconfigs = instance.process_groups_from_parser(config)
  1764. self.assertEqual(len(gconfigs), 1)
  1765. gconfig = gconfigs[0]
  1766. self.assertEqual(gconfig.name, 'thegroup')
  1767. self.assertEqual(gconfig.priority, 5)
  1768. self.assertEqual(len(gconfig.process_configs), 2)
  1769. def test_mixed_process_groups_from_parser1(self):
  1770. text = lstrip("""\
  1771. [program:one]
  1772. command = /bin/cat
  1773. [program:two]
  1774. command = /bin/cat
  1775. [program:many]
  1776. process_name = %(program_name)s_%(process_num)s
  1777. command = /bin/cat
  1778. numprocs = 2
  1779. priority = 1
  1780. [group:thegroup]
  1781. programs = one,two
  1782. priority = 5
  1783. """)
  1784. from supervisor.options import UnhosedConfigParser
  1785. config = UnhosedConfigParser()
  1786. config.read_string(text)
  1787. instance = self._makeOne()
  1788. gconfigs = instance.process_groups_from_parser(config)
  1789. self.assertEqual(len(gconfigs), 2)
  1790. manyconfig = gconfigs[0]
  1791. self.assertEqual(manyconfig.name, 'many')
  1792. self.assertEqual(manyconfig.priority, 1)
  1793. self.assertEqual(len(manyconfig.process_configs), 2)
  1794. gconfig = gconfigs[1]
  1795. self.assertEqual(gconfig.name, 'thegroup')
  1796. self.assertEqual(gconfig.priority, 5)
  1797. self.assertEqual(len(gconfig.process_configs), 2)
  1798. def test_mixed_process_groups_from_parser2(self):
  1799. text = lstrip("""\
  1800. [program:one]
  1801. command = /bin/cat
  1802. [program:two]
  1803. command = /bin/cat
  1804. [program:many]
  1805. process_name = %(program_name)s_%(process_num)s
  1806. command = /bin/cat
  1807. numprocs = 2
  1808. priority = 1
  1809. [group:thegroup]
  1810. programs = one,two, many
  1811. priority = 5
  1812. """)
  1813. from supervisor.options import UnhosedConfigParser
  1814. config = UnhosedConfigParser()
  1815. config.read_string(text)
  1816. instance = self._makeOne()
  1817. gconfigs = instance.process_groups_from_parser(config)
  1818. self.assertEqual(len(gconfigs), 1)
  1819. gconfig = gconfigs[0]
  1820. self.assertEqual(gconfig.name, 'thegroup')
  1821. self.assertEqual(gconfig.priority, 5)
  1822. self.assertEqual(len(gconfig.process_configs), 4)
  1823. def test_unknown_program_in_heterogeneous_group(self):
  1824. text = lstrip("""\
  1825. [program:one]
  1826. command = /bin/cat
  1827. [group:foo]
  1828. programs = notthere
  1829. """)
  1830. from supervisor.options import UnhosedConfigParser
  1831. config = UnhosedConfigParser()
  1832. config.read_string(text)
  1833. instance = self._makeOne()
  1834. self.assertRaises(ValueError, instance.process_groups_from_parser,
  1835. config)
  1836. def test_rpcinterfaces_from_parser(self):
  1837. text = lstrip("""\
  1838. [rpcinterface:dummy]
  1839. supervisor.rpcinterface_factory = %s
  1840. foo = bar
  1841. baz = qux
  1842. """ % __name__)
  1843. from supervisor.options import UnhosedConfigParser
  1844. config = UnhosedConfigParser()
  1845. config.read_string(text)
  1846. instance = self._makeOne()
  1847. factories = instance.get_plugins(config,
  1848. 'supervisor.rpcinterface_factory',
  1849. 'rpcinterface:')
  1850. self.assertEqual(len(factories), 1)
  1851. factory = factories[0]
  1852. self.assertEqual(factory[0], 'dummy')
  1853. self.assertEqual(factory[1], sys.modules[__name__])
  1854. self.assertEqual(factory[2], {'foo':'bar', 'baz':'qux'})
  1855. def test_rpcinterfaces_from_parser_factory_expansions(self):
  1856. text = lstrip("""\
  1857. [rpcinterface:dummy]
  1858. supervisor.rpcinterface_factory = %(factory)s
  1859. foo = %(pet)s
  1860. """)
  1861. from supervisor.options import UnhosedConfigParser
  1862. instance = self._makeOne()
  1863. config = UnhosedConfigParser()
  1864. config.expansions = {'factory': __name__, 'pet': 'cat'}
  1865. config.read_string(text)
  1866. factories = instance.get_plugins(config,
  1867. 'supervisor.rpcinterface_factory',
  1868. 'rpcinterface:')
  1869. self.assertEqual(len(factories), 1)
  1870. factory = factories[0]
  1871. self.assertEqual(factory[0], 'dummy')
  1872. self.assertEqual(factory[1], sys.modules[__name__])
  1873. self.assertEqual(factory[2], {'foo': 'cat'})
  1874. def test_rpcinterfaces_from_parser_factory_missing(self):
  1875. text = lstrip("""\
  1876. [rpcinterface:dummy]
  1877. # note: no supervisor.rpcinterface_factory here
  1878. """)
  1879. from supervisor.options import UnhosedConfigParser
  1880. config = UnhosedConfigParser()
  1881. config.read_string(text)
  1882. instance = self._makeOne()
  1883. try:
  1884. instance.get_plugins(config,
  1885. 'supervisor.rpcinterface_factory',
  1886. 'rpcinterface:')
  1887. self.fail('nothing raised')
  1888. except ValueError as exc:
  1889. self.assertEqual(exc.args[0], 'section [rpcinterface:dummy] '
  1890. 'does not specify a supervisor.rpcinterface_factory')
  1891. def test_rpcinterfaces_from_parser_factory_not_importable(self):
  1892. text = lstrip("""\
  1893. [rpcinterface:dummy]
  1894. supervisor.rpcinterface_factory = nonexistant
  1895. """)
  1896. from supervisor.options import UnhosedConfigParser
  1897. config = UnhosedConfigParser()
  1898. config.read_string(text)
  1899. instance = self._makeOne()
  1900. try:
  1901. instance.get_plugins(config,
  1902. 'supervisor.rpcinterface_factory',
  1903. 'rpcinterface:')
  1904. self.fail('nothing raised')
  1905. except ValueError as exc:
  1906. self.assertEqual(exc.args[0], 'nonexistant cannot be resolved '
  1907. 'within [rpcinterface:dummy]')
  1908. def test_clear_autochildlogdir(self):
  1909. dn = tempfile.mkdtemp()
  1910. try:
  1911. instance = self._makeOne()
  1912. instance.childlogdir = dn
  1913. sid = 'supervisor'
  1914. instance.identifier = sid
  1915. logfn = instance.get_autochildlog_name('foo', sid,'stdout')
  1916. first = logfn + '.1'
  1917. second = logfn + '.2'
  1918. f1 = open(first, 'w')
  1919. f2 = open(second, 'w')
  1920. instance.clear_autochildlogdir()
  1921. self.assertFalse(os.path.exists(logfn))
  1922. self.assertFalse(os.path.exists(first))
  1923. self.assertFalse(os.path.exists(second))
  1924. f1.close()
  1925. f2.close()
  1926. finally:
  1927. shutil.rmtree(dn)
  1928. def test_clear_autochildlog_oserror(self):
  1929. instance = self._makeOne()
  1930. instance.childlogdir = '/tmp/this/cant/possibly/existjjjj'
  1931. instance.logger = DummyLogger()
  1932. instance.clear_autochildlogdir()
  1933. self.assertEqual(instance.logger.data, ['Could not clear childlog dir'])
  1934. def test_openhttpservers_reports_friendly_usage_when_eaddrinuse(self):
  1935. supervisord = DummySupervisor()
  1936. instance = self._makeOne()
  1937. def raise_eaddrinuse(supervisord):
  1938. raise socket.error(errno.EADDRINUSE)
  1939. instance.make_http_servers = raise_eaddrinuse
  1940. recorder = []
  1941. def record_usage(message):
  1942. recorder.append(message)
  1943. instance.usage = record_usage
  1944. instance.openhttpservers(supervisord)
  1945. self.assertEqual(len(recorder), 1)
  1946. expected = 'Another program is already listening'
  1947. self.assertTrue(recorder[0].startswith(expected))
  1948. def test_openhttpservers_reports_socket_error_with_errno(self):
  1949. supervisord = DummySupervisor()
  1950. instance = self._makeOne()
  1951. def make_http_servers(supervisord):
  1952. raise socket.error(errno.EPERM)
  1953. instance.make_http_servers = make_http_servers
  1954. recorder = []
  1955. def record_usage(message):
  1956. recorder.append(message)
  1957. instance.usage = record_usage
  1958. instance.openhttpservers(supervisord)
  1959. self.assertEqual(len(recorder), 1)
  1960. expected = ('Cannot open an HTTP server: socket.error '
  1961. 'reported errno.EPERM (%d)' % errno.EPERM)
  1962. self.assertEqual(recorder[0], expected)
  1963. def test_openhttpservers_reports_other_socket_errors(self):
  1964. supervisord = DummySupervisor()
  1965. instance = self._makeOne()
  1966. def make_http_servers(supervisord):
  1967. raise socket.error('uh oh')
  1968. instance.make_http_servers = make_http_servers
  1969. recorder = []
  1970. def record_usage(message):
  1971. recorder.append(message)
  1972. instance.usage = record_usage
  1973. instance.openhttpservers(supervisord)
  1974. self.assertEqual(len(recorder), 1)
  1975. expected = ('Cannot open an HTTP server: socket.error '
  1976. 'reported uh oh')
  1977. self.assertEqual(recorder[0], expected)
  1978. def test_openhttpservers_reports_value_errors(self):
  1979. supervisord = DummySupervisor()
  1980. instance = self._makeOne()
  1981. def make_http_servers(supervisord):
  1982. raise ValueError('not prefixed with help')
  1983. instance.make_http_servers = make_http_servers
  1984. recorder = []
  1985. def record_usage(message):
  1986. recorder.append(message)
  1987. instance.usage = record_usage
  1988. instance.openhttpservers(supervisord)
  1989. self.assertEqual(len(recorder), 1)
  1990. expected = 'not prefixed with help'
  1991. self.assertEqual(recorder[0], expected)
  1992. def test_openhttpservers_does_not_catch_other_exception_types(self):
  1993. supervisord = DummySupervisor()
  1994. instance = self._makeOne()
  1995. def make_http_servers(supervisord):
  1996. raise OverflowError
  1997. instance.make_http_servers = make_http_servers
  1998. # this scenario probably means a bug in supervisor. we dump
  1999. # all the gory details on the poor user for troubleshooting
  2000. self.assertRaises(OverflowError,
  2001. instance.openhttpservers, supervisord)
  2002. def test_dropPrivileges_user_none(self):
  2003. instance = self._makeOne()
  2004. msg = instance.dropPrivileges(None)
  2005. self.assertEqual(msg, "No user specified to setuid to!")
  2006. @patch('pwd.getpwuid', Mock(return_value=["foo", None, 12, 34]))
  2007. @patch('os.getuid', Mock(return_value=12))
  2008. def test_dropPrivileges_nonroot_same_user(self):
  2009. instance = self._makeOne()
  2010. msg = instance.dropPrivileges(os.getuid())
  2011. self.assertEqual(msg, None) # no error if same user
  2012. @patch('pwd.getpwuid', Mock(return_value=["foo", None, 55, 34]))
  2013. @patch('os.getuid', Mock(return_value=12))
  2014. def test_dropPrivileges_nonroot_different_user(self):
  2015. instance = self._makeOne()
  2016. msg = instance.dropPrivileges(42)
  2017. self.assertEqual(msg, "Can't drop privilege as nonroot user")
  2018. def test_daemonize_notifies_poller_before_and_after_fork(self):
  2019. instance = self._makeOne()
  2020. instance._daemonize = lambda: None
  2021. instance.poller = Mock()
  2022. instance.daemonize()
  2023. instance.poller.before_daemonize.assert_called_once_with()
  2024. instance.poller.after_daemonize.assert_called_once_with()
  2025. class TestProcessConfig(unittest.TestCase):
  2026. def _getTargetClass(self):
  2027. from supervisor.options import ProcessConfig
  2028. return ProcessConfig
  2029. def _makeOne(self, *arg, **kw):
  2030. defaults = {}
  2031. for name in ('name', 'command', 'directory', 'umask',
  2032. 'priority', 'autostart', 'autorestart',
  2033. 'startsecs', 'startretries', 'uid',
  2034. 'stdout_logfile', 'stdout_capture_maxbytes',
  2035. 'stdout_events_enabled', 'stdout_syslog',
  2036. 'stderr_logfile', 'stderr_capture_maxbytes',
  2037. 'stderr_events_enabled', 'stderr_syslog',
  2038. 'stopsignal', 'stopwaitsecs', 'stopasgroup',
  2039. 'killasgroup', 'exitcodes', 'redirect_stderr',
  2040. 'environment'):
  2041. defaults[name] = name
  2042. for name in ('stdout_logfile_backups', 'stdout_logfile_maxbytes',
  2043. 'stderr_logfile_backups', 'stderr_logfile_maxbytes'):
  2044. defaults[name] = 10
  2045. defaults.update(kw)
  2046. return self._getTargetClass()(*arg, **defaults)
  2047. def test_create_autochildlogs(self):
  2048. options = DummyOptions()
  2049. instance = self._makeOne(options)
  2050. from supervisor.datatypes import Automatic
  2051. instance.stdout_logfile = Automatic
  2052. instance.stderr_logfile = Automatic
  2053. instance.create_autochildlogs()
  2054. self.assertEqual(instance.stdout_logfile, options.tempfile_name)
  2055. self.assertEqual(instance.stderr_logfile, options.tempfile_name)
  2056. def test_make_process(self):
  2057. options = DummyOptions()
  2058. instance = self._makeOne(options)
  2059. process = instance.make_process()
  2060. from supervisor.process import Subprocess
  2061. self.assertEqual(process.__class__, Subprocess)
  2062. self.assertEqual(process.group, None)
  2063. def test_make_process_with_group(self):
  2064. options = DummyOptions()
  2065. instance = self._makeOne(options)
  2066. process = instance.make_process('abc')
  2067. from supervisor.process import Subprocess
  2068. self.assertEqual(process.__class__, Subprocess)
  2069. self.assertEqual(process.group, 'abc')
  2070. def test_make_dispatchers_stderr_not_redirected(self):
  2071. options = DummyOptions()
  2072. instance = self._makeOne(options)
  2073. with _getTempFile('stderr_logfile') as stdout_logfile:
  2074. with _getTempFile('stderr_logfile') as stderr_logfile:
  2075. instance.stdout_logfile = stdout_logfile.name
  2076. instance.stderr_logfile = stderr_logfile.name
  2077. logger.debug('instance.stdout_logfile = %r',
  2078. instance.stdout_logfile)
  2079. logger.debug('instance.stderr_logfile = %r',
  2080. instance.stderr_logfile)
  2081. instance.redirect_stderr = False
  2082. process1 = DummyProcess(instance)
  2083. dispatchers, pipes = instance.make_dispatchers(process1)
  2084. self.assertEqual(dispatchers[5].channel, 'stdout')
  2085. from supervisor.events import ProcessCommunicationStdoutEvent
  2086. self.assertEqual(dispatchers[5].event_type,
  2087. ProcessCommunicationStdoutEvent)
  2088. self.assertEqual(pipes['stdout'], 5)
  2089. self.assertEqual(dispatchers[7].channel, 'stderr')
  2090. from supervisor.events import ProcessCommunicationStderrEvent
  2091. self.assertEqual(dispatchers[7].event_type,
  2092. ProcessCommunicationStderrEvent)
  2093. self.assertEqual(pipes['stderr'], 7)
  2094. def test_make_dispatchers_stderr_redirected(self):
  2095. options = DummyOptions()
  2096. instance = self._makeOne(options)
  2097. with _getTempFile('stderr_logfile') as stdout_logfile:
  2098. instance.stdout_logfile = stdout_logfile.name
  2099. logger.debug('instance.stdout_logfile = %r',
  2100. instance.stdout_logfile)
  2101. process1 = DummyProcess(instance)
  2102. dispatchers, pipes = instance.make_dispatchers(process1)
  2103. self.assertEqual(dispatchers[5].channel, 'stdout')
  2104. self.assertEqual(pipes['stdout'], 5)
  2105. self.assertEqual(pipes['stderr'], None)
  2106. class FastCGIProcessConfigTest(unittest.TestCase):
  2107. def _getTargetClass(self):
  2108. from supervisor.options import FastCGIProcessConfig
  2109. return FastCGIProcessConfig
  2110. def _makeOne(self, *arg, **kw):
  2111. defaults = {}
  2112. for name in ('name', 'command', 'directory', 'umask',
  2113. 'priority', 'autostart', 'autorestart',
  2114. 'startsecs', 'startretries', 'uid',
  2115. 'stdout_logfile', 'stdout_capture_maxbytes',
  2116. 'stdout_events_enabled', 'stdout_syslog',
  2117. 'stderr_logfile', 'stderr_capture_maxbytes',
  2118. 'stderr_events_enabled', 'stderr_syslog',
  2119. 'stopsignal', 'stopwaitsecs', 'stopasgroup',
  2120. 'killasgroup', 'exitcodes', 'redirect_stderr',
  2121. 'environment'):
  2122. defaults[name] = name
  2123. for name in ('stdout_logfile_backups', 'stdout_logfile_maxbytes',
  2124. 'stderr_logfile_backups', 'stderr_logfile_maxbytes'):
  2125. defaults[name] = 10
  2126. defaults.update(kw)
  2127. return self._getTargetClass()(*arg, **defaults)
  2128. def test_make_process(self):
  2129. options = DummyOptions()
  2130. instance = self._makeOne(options)
  2131. self.assertRaises(NotImplementedError, instance.make_process)
  2132. def test_make_process_with_group(self):
  2133. options = DummyOptions()
  2134. instance = self._makeOne(options)
  2135. process = instance.make_process('abc')
  2136. from supervisor.process import FastCGISubprocess
  2137. self.assertEqual(process.__class__, FastCGISubprocess)
  2138. self.assertEqual(process.group, 'abc')
  2139. def test_make_dispatchers(self):
  2140. options = DummyOptions()
  2141. instance = self._makeOne(options)
  2142. with _getTempFile('stderr_logfile') as stdout_logfile:
  2143. with _getTempFile('stderr_logfile') as stderr_logfile:
  2144. instance.stdout_logfile = stdout_logfile.name
  2145. instance.stderr_logfile = stderr_logfile.name
  2146. logger.debug('instance.stdout_logfile = %r',
  2147. instance.stdout_logfile)
  2148. logger.debug('instance.stderr_logfile = %r',
  2149. instance.stderr_logfile)
  2150. instance.redirect_stderr = False
  2151. process1 = DummyProcess(instance)
  2152. dispatchers, pipes = instance.make_dispatchers(process1)
  2153. self.assertEqual(dispatchers[4].channel, 'stdin')
  2154. self.assertEqual(dispatchers[4].closed, True)
  2155. self.assertEqual(dispatchers[5].channel, 'stdout')
  2156. from supervisor.events import ProcessCommunicationStdoutEvent
  2157. self.assertEqual(dispatchers[5].event_type,
  2158. ProcessCommunicationStdoutEvent)
  2159. self.assertEqual(pipes['stdout'], 5)
  2160. self.assertEqual(dispatchers[7].channel, 'stderr')
  2161. from supervisor.events import ProcessCommunicationStderrEvent
  2162. self.assertEqual(dispatchers[7].event_type,
  2163. ProcessCommunicationStderrEvent)
  2164. self.assertEqual(pipes['stderr'], 7)
  2165. class ProcessGroupConfigTests(unittest.TestCase):
  2166. def _getTargetClass(self):
  2167. from supervisor.options import ProcessGroupConfig
  2168. return ProcessGroupConfig
  2169. def _makeOne(self, options, name, priority, pconfigs):
  2170. return self._getTargetClass()(options, name, priority, pconfigs)
  2171. def test_ctor(self):
  2172. options = DummyOptions()
  2173. instance = self._makeOne(options, 'whatever', 999, [])
  2174. self.assertEqual(instance.options, options)
  2175. self.assertEqual(instance.name, 'whatever')
  2176. self.assertEqual(instance.priority, 999)
  2177. self.assertEqual(instance.process_configs, [])
  2178. def test_after_setuid(self):
  2179. options = DummyOptions()
  2180. pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
  2181. instance = self._makeOne(options, 'whatever', 999, pconfigs)
  2182. instance.after_setuid()
  2183. self.assertEqual(pconfigs[0].autochildlogs_created, True)
  2184. def test_make_group(self):
  2185. options = DummyOptions()
  2186. pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
  2187. instance = self._makeOne(options, 'whatever', 999, pconfigs)
  2188. group = instance.make_group()
  2189. from supervisor.process import ProcessGroup
  2190. self.assertEqual(group.__class__, ProcessGroup)
  2191. class EventListenerPoolConfigTests(unittest.TestCase):
  2192. def _getTargetClass(self):
  2193. from supervisor.options import EventListenerPoolConfig
  2194. return EventListenerPoolConfig
  2195. def _makeOne(self, options, name, priority, process_configs, buffer_size,
  2196. pool_events, result_handler):
  2197. return self._getTargetClass()(options, name, priority,
  2198. process_configs, buffer_size,
  2199. pool_events, result_handler)
  2200. def test_make_group(self):
  2201. options = DummyOptions()
  2202. pconfigs = [DummyPConfig(options, 'process1', '/bin/process1')]
  2203. instance = self._makeOne(options, 'name', 999, pconfigs, 1, [], None)
  2204. group = instance.make_group()
  2205. from supervisor.process import EventListenerPool
  2206. self.assertEqual(group.__class__, EventListenerPool)
  2207. class FastCGIGroupConfigTests(unittest.TestCase):
  2208. def _getTargetClass(self):
  2209. from supervisor.options import FastCGIGroupConfig
  2210. return FastCGIGroupConfig
  2211. def _makeOne(self, *args, **kw):
  2212. return self._getTargetClass()(*args, **kw)
  2213. def test_ctor(self):
  2214. options = DummyOptions()
  2215. sock_config = DummySocketConfig(6)
  2216. instance = self._makeOne(options, 'whatever', 999, [], sock_config)
  2217. self.assertEqual(instance.options, options)
  2218. self.assertEqual(instance.name, 'whatever')
  2219. self.assertEqual(instance.priority, 999)
  2220. self.assertEqual(instance.process_configs, [])
  2221. self.assertEqual(instance.socket_config, sock_config)
  2222. def test_same_sockets_are_equal(self):
  2223. options = DummyOptions()
  2224. sock_config1 = DummySocketConfig(6)
  2225. instance1 = self._makeOne(options, 'whatever', 999, [], sock_config1)
  2226. sock_config2 = DummySocketConfig(6)
  2227. instance2 = self._makeOne(options, 'whatever', 999, [], sock_config2)
  2228. self.assertTrue(instance1 == instance2)
  2229. self.assertFalse(instance1 != instance2)
  2230. def test_diff_sockets_are_not_equal(self):
  2231. options = DummyOptions()
  2232. sock_config1 = DummySocketConfig(6)
  2233. instance1 = self._makeOne(options, 'whatever', 999, [], sock_config1)
  2234. sock_config2 = DummySocketConfig(7)
  2235. instance2 = self._makeOne(options, 'whatever', 999, [], sock_config2)
  2236. self.assertTrue(instance1 != instance2)
  2237. self.assertFalse(instance1 == instance2)
  2238. def test_make_group(self):
  2239. options = DummyOptions()
  2240. sock_config = DummySocketConfig(6)
  2241. instance = self._makeOne(options, 'name', 999, [], sock_config)
  2242. group = instance.make_group()
  2243. from supervisor.process import FastCGIProcessGroup
  2244. self.assertEqual(group.__class__, FastCGIProcessGroup)
  2245. class SignalReceiverTests(unittest.TestCase):
  2246. def test_returns_None_initially(self):
  2247. from supervisor.options import SignalReceiver
  2248. sr = SignalReceiver()
  2249. self.assertEqual(sr.get_signal(), None)
  2250. def test_returns_signals_in_order_received(self):
  2251. from supervisor.options import SignalReceiver
  2252. sr = SignalReceiver()
  2253. sr.receive(signal.SIGTERM, 'frame')
  2254. sr.receive(signal.SIGCHLD, 'frame')
  2255. self.assertEqual(sr.get_signal(), signal.SIGTERM)
  2256. self.assertEqual(sr.get_signal(), signal.SIGCHLD)
  2257. self.assertEqual(sr.get_signal(), None)
  2258. def test_does_not_queue_duplicate_signals(self):
  2259. from supervisor.options import SignalReceiver
  2260. sr = SignalReceiver()
  2261. sr.receive(signal.SIGTERM, 'frame')
  2262. sr.receive(signal.SIGTERM, 'frame')
  2263. self.assertEqual(sr.get_signal(), signal.SIGTERM)
  2264. self.assertEqual(sr.get_signal(), None)
  2265. def test_queues_again_after_being_emptied(self):
  2266. from supervisor.options import SignalReceiver
  2267. sr = SignalReceiver()
  2268. sr.receive(signal.SIGTERM, 'frame')
  2269. self.assertEqual(sr.get_signal(), signal.SIGTERM)
  2270. self.assertEqual(sr.get_signal(), None)
  2271. sr.receive(signal.SIGCHLD, 'frame')
  2272. self.assertEqual(sr.get_signal(), signal.SIGCHLD)
  2273. self.assertEqual(sr.get_signal(), None)
  2274. class UnhosedConfigParserTests(unittest.TestCase):
  2275. def _getTargetClass(self):
  2276. from supervisor.options import UnhosedConfigParser
  2277. return UnhosedConfigParser
  2278. def _makeOne(self, *args, **kw):
  2279. return self._getTargetClass()(*args, **kw)
  2280. def test_saneget_no_default(self):
  2281. parser = self._makeOne()
  2282. parser.read_string("[supervisord]\n")
  2283. from supervisor.compat import ConfigParser
  2284. self.assertRaises(ConfigParser.NoOptionError,
  2285. parser.saneget, "supervisord", "missing")
  2286. def test_saneget_with_default(self):
  2287. parser = self._makeOne()
  2288. parser.read_string("[supervisord]\n")
  2289. result = parser.saneget("supervisord", "missing", default="abc")
  2290. self.assertEqual(result, "abc")
  2291. def test_saneget_with_default_and_expand(self):
  2292. parser = self._makeOne()
  2293. parser.expansions = {'pet': 'dog'}
  2294. parser.read_string("[supervisord]\n")
  2295. result = parser.saneget("supervisord", "foo", default="%(pet)s")
  2296. self.assertEqual(result, "dog")
  2297. def test_saneget_with_default_no_expand(self):
  2298. parser = self._makeOne()
  2299. parser.expansions = {'pet': 'dog'}
  2300. parser.read_string("[supervisord]\n")
  2301. result = parser.saneget("supervisord", "foo",
  2302. default="%(pet)s", do_expand=False)
  2303. self.assertEqual(result, "%(pet)s")
  2304. def test_saneget_no_default_no_expand(self):
  2305. parser = self._makeOne()
  2306. parser.read_string("[supervisord]\nfoo=%(pet)s\n")
  2307. result = parser.saneget("supervisord", "foo", do_expand=False)
  2308. self.assertEqual(result, "%(pet)s")
  2309. def test_saneget_expands_instance_expansions(self):
  2310. parser = self._makeOne()
  2311. parser.expansions = {'pet': 'dog'}
  2312. parser.read_string("[supervisord]\nfoo=%(pet)s\n")
  2313. result = parser.saneget("supervisord", "foo")
  2314. self.assertEqual(result, "dog")
  2315. def test_saneget_expands_arg_expansions(self):
  2316. parser = self._makeOne()
  2317. parser.expansions = {'pet': 'dog'}
  2318. parser.read_string("[supervisord]\nfoo=%(pet)s\n")
  2319. result = parser.saneget("supervisord", "foo",
  2320. expansions={'pet': 'cat'})
  2321. self.assertEqual(result, "cat")
  2322. def test_getdefault_does_saneget_with_mysection(self):
  2323. parser = self._makeOne()
  2324. parser.read_string("[%s]\nfoo=bar\n" % parser.mysection)
  2325. self.assertEqual(parser.getdefault("foo"), "bar")
  2326. def test_read_filenames_as_string(self):
  2327. f = tempfile.NamedTemporaryFile(mode="w+")
  2328. parser = self._makeOne()
  2329. try:
  2330. f.write("[foo]\n")
  2331. f.flush()
  2332. ok_filenames = parser.read(f.name)
  2333. finally:
  2334. f.close()
  2335. self.assertEqual(ok_filenames, [f.name])
  2336. def test_read_filenames_as_list(self):
  2337. f = tempfile.NamedTemporaryFile(mode="w+")
  2338. parser = self._makeOne()
  2339. try:
  2340. f.write("[foo]\n")
  2341. f.flush()
  2342. ok_filenames = parser.read([f.name])
  2343. finally:
  2344. f.close()
  2345. self.assertEqual(ok_filenames, [f.name])
  2346. def test_read_returns_ok_filenames_like_rawconfigparser(self):
  2347. nonexistant = os.path.join(os.path.dirname(__file__), "nonexistant")
  2348. f = tempfile.NamedTemporaryFile(mode="w+")
  2349. parser = self._makeOne()
  2350. try:
  2351. f.write("[foo]\n")
  2352. f.flush()
  2353. ok_filenames = parser.read([nonexistant, f.name])
  2354. finally:
  2355. f.close()
  2356. self.assertEqual(ok_filenames, [f.name])
  2357. def test_read_section_to_file_initially_empty(self):
  2358. parser = self._makeOne()
  2359. self.assertEqual(parser.section_to_file, {})
  2360. def test_read_section_to_file_read_one_file(self):
  2361. f = tempfile.NamedTemporaryFile(mode="w+")
  2362. try:
  2363. f.write("[foo]\n")
  2364. f.flush()
  2365. parser = self._makeOne()
  2366. parser.read([f.name])
  2367. finally:
  2368. f.close()
  2369. self.assertEqual(parser.section_to_file['foo'], f.name)
  2370. def test_read_section_to_file_read_multiple_files(self):
  2371. f1 = tempfile.NamedTemporaryFile(mode="w+")
  2372. f2 = tempfile.NamedTemporaryFile(mode="w+")
  2373. try:
  2374. f1.write("[foo]\n")
  2375. f1.flush()
  2376. f2.write("[bar]\n")
  2377. f2.flush()
  2378. parser = self._makeOne()
  2379. parser.read([f1.name, f2.name])
  2380. finally:
  2381. f1.close()
  2382. f2.close()
  2383. self.assertEqual(parser.section_to_file['foo'], f1.name)
  2384. self.assertEqual(parser.section_to_file['bar'], f2.name)
  2385. class UtilFunctionsTests(unittest.TestCase):
  2386. def test_make_namespec(self):
  2387. from supervisor.options import make_namespec
  2388. self.assertEqual(make_namespec('group', 'process'), 'group:process')
  2389. self.assertEqual(make_namespec('process', 'process'), 'process')
  2390. def test_split_namespec(self):
  2391. from supervisor.options import split_namespec
  2392. s = split_namespec
  2393. self.assertEqual(s('process:group'), ('process', 'group'))
  2394. self.assertEqual(s('process'), ('process', 'process'))
  2395. self.assertEqual(s('group:'), ('group', None))
  2396. self.assertEqual(s('group:*'), ('group', None))
  2397. def test_suite():
  2398. return unittest.findTestCases(sys.modules[__name__])
  2399. if __name__ == '__main__':
  2400. unittest.main(defaultTest='test_suite')