test_options.py 117 KB

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