test_supervisorctl.py 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628
  1. import sys
  2. import unittest
  3. from StringIO import StringIO
  4. from supervisor.tests.base import DummyRPCServer
  5. class ControllerTests(unittest.TestCase):
  6. def _getTargetClass(self):
  7. from supervisor.supervisorctl import Controller
  8. return Controller
  9. def _makeOne(self, options):
  10. return self._getTargetClass()(options)
  11. def test_ctor(self):
  12. options = DummyClientOptions()
  13. controller = self._makeOne(options)
  14. self.assertEqual(controller.prompt, options.prompt + '> ')
  15. def test__upcheck(self):
  16. options = DummyClientOptions()
  17. controller = self._makeOne(options)
  18. result = controller.upcheck()
  19. self.assertEqual(result, True)
  20. def test__upcheck_wrong_server_version(self):
  21. options = DummyClientOptions()
  22. options._server.supervisor.getVersion = lambda *x: '1.0'
  23. controller = self._makeOne(options)
  24. controller.stdout = StringIO()
  25. result = controller.upcheck()
  26. self.assertEqual(result, False)
  27. self.assertEqual(controller.stdout.getvalue(),
  28. 'Sorry, this version of supervisorctl expects'
  29. ' to talk to a server with API version 3.0, but'
  30. ' the remote version is 1.0.\n')
  31. def test__upcheck_unknown_method(self):
  32. options = DummyClientOptions()
  33. from xmlrpclib import Fault
  34. from supervisor.xmlrpc import Faults
  35. def getVersion():
  36. raise Fault(Faults.UNKNOWN_METHOD, 'duh')
  37. options._server.supervisor.getVersion = getVersion
  38. controller = self._makeOne(options)
  39. controller.stdout = StringIO()
  40. result = controller.upcheck()
  41. self.assertEqual(result, False)
  42. self.assertEqual(controller.stdout.getvalue(),
  43. 'Sorry, supervisord responded but did not recognize'
  44. ' the supervisor namespace commands that'
  45. ' supervisorctl uses to control it. Please check'
  46. ' that the [rpcinterface:supervisor] section is'
  47. ' enabled in the configuration file'
  48. ' (see sample.conf).\n')
  49. def test__upcheck_catches_socket_error_ECONNREFUSED(self):
  50. options = DummyClientOptions()
  51. import socket
  52. import errno
  53. def raise_fault(*arg, **kw):
  54. raise socket.error(errno.ECONNREFUSED, 'nobody home')
  55. options._server.supervisor.getVersion = raise_fault
  56. controller = self._makeOne(options)
  57. controller.stdout = StringIO()
  58. result = controller.upcheck()
  59. self.assertEqual(result, False)
  60. output = controller.stdout.getvalue()
  61. self.assertTrue('refused connection' in output)
  62. def test__upcheck_catches_socket_error_ENOENT(self):
  63. options = DummyClientOptions()
  64. import socket
  65. import errno
  66. def raise_fault(*arg, **kw):
  67. raise socket.error(errno.ENOENT, 'nobody home')
  68. options._server.supervisor.getVersion = raise_fault
  69. controller = self._makeOne(options)
  70. controller.stdout = StringIO()
  71. result = controller.upcheck()
  72. self.assertEqual(result, False)
  73. output = controller.stdout.getvalue()
  74. self.assertTrue('no such file' in output)
  75. def test_onecmd(self):
  76. options = DummyClientOptions()
  77. controller = self._makeOne(options)
  78. controller.stdout = StringIO()
  79. plugin = DummyPlugin()
  80. controller.options.plugins = (plugin,)
  81. result = controller.onecmd('help')
  82. self.assertEqual(result, None)
  83. self.assertEqual(plugin.helped, True)
  84. def test_onecmd_multi_colonseparated(self):
  85. options = DummyClientOptions()
  86. controller = self._makeOne(options)
  87. controller.stdout = StringIO()
  88. plugin = DummyPlugin()
  89. controller.options.plugins = (plugin,)
  90. result = controller.onecmd('help; help')
  91. self.assertEqual(result, None)
  92. self.assertEqual(controller.cmdqueue, [' help'])
  93. self.assertEqual(plugin.helped, True)
  94. def test_onecmd_empty_does_not_repeat_previous_cmd(self):
  95. options = DummyClientOptions()
  96. controller = self._makeOne(options)
  97. controller.stdout = StringIO()
  98. plugin = DummyPlugin()
  99. controller.options.plugins = (plugin,)
  100. plugin.helped = False
  101. controller.onecmd('help')
  102. self.assertTrue(plugin.helped)
  103. plugin.helped = False
  104. controller.onecmd('')
  105. self.assertFalse(plugin.helped)
  106. def test_onecmd_clears_completion_cache(self):
  107. options = DummyClientOptions()
  108. controller = self._makeOne(options)
  109. controller.stdout = StringIO()
  110. controller._complete_info = {}
  111. controller.onecmd('help')
  112. self.assertEqual(controller._complete_info, None)
  113. def test_complete_action_empty(self):
  114. options = DummyClientOptions()
  115. controller = self._makeOne(options)
  116. controller.stdout=StringIO()
  117. controller.vocab = ['help']
  118. result = controller.complete('', 0, line='')
  119. self.assertEqual(result, 'help ')
  120. result = controller.complete('', 1, line='')
  121. self.assertEqual(result, None)
  122. def test_complete_action_partial(self):
  123. options = DummyClientOptions()
  124. controller = self._makeOne(options)
  125. controller.stdout=StringIO()
  126. controller.vocab = ['help']
  127. result = controller.complete('h', 0, line='h')
  128. self.assertEqual(result, 'help ')
  129. result = controller.complete('h', 1, line='h')
  130. self.assertEqual(result, None)
  131. def test_complete_action_whole(self):
  132. options = DummyClientOptions()
  133. controller = self._makeOne(options)
  134. controller.stdout=StringIO()
  135. controller.vocab = ['help']
  136. result = controller.complete('help', 0, line='help')
  137. self.assertEqual(result, 'help ')
  138. def test_complete_unknown_action_uncompletable(self):
  139. options = DummyClientOptions()
  140. controller = self._makeOne(options)
  141. controller.stdout=StringIO()
  142. result = controller.complete('bad', 0, line='bad')
  143. self.assertEqual(result, None)
  144. def test_complete_unknown_action_arg_uncompletable(self):
  145. options = DummyClientOptions()
  146. controller = self._makeOne(options)
  147. controller.stdout=StringIO()
  148. controller.vocab = ['help', 'add']
  149. result = controller.complete('', 1, line='bad ')
  150. self.assertEqual(result, None)
  151. def test_complete_help_empty(self):
  152. options = DummyClientOptions()
  153. controller = self._makeOne(options)
  154. controller.stdout=StringIO()
  155. controller.vocab = ['help', 'start']
  156. result = controller.complete('', 0, line='help ')
  157. self.assertEqual(result, 'help ')
  158. result = controller.complete('', 1, line='help ')
  159. self.assertEqual(result, 'start ')
  160. result = controller.complete('', 2, line='help ')
  161. self.assertEqual(result, None)
  162. def test_complete_help_action(self):
  163. options = DummyClientOptions()
  164. controller = self._makeOne(options)
  165. controller.stdout=StringIO()
  166. controller.vocab = ['help', 'start']
  167. result = controller.complete('he', 0, line='help he')
  168. self.assertEqual(result, 'help ')
  169. result = controller.complete('he', 1, line='help he')
  170. self.assertEqual(result, None)
  171. def test_complete_start_empty(self):
  172. options = DummyClientOptions()
  173. controller = self._makeOne(options)
  174. controller.stdout=StringIO()
  175. controller.vocab = ['help', 'start']
  176. result = controller.complete('', 0, line='start ')
  177. self.assertEqual(result, 'foo ')
  178. result = controller.complete('', 1, line='start ')
  179. self.assertEqual(result, 'bar ')
  180. result = controller.complete('', 2, line='start ')
  181. self.assertEqual(result, 'baz:baz_01 ')
  182. result = controller.complete('', 3, line='start ')
  183. self.assertEqual(result, 'baz:* ')
  184. result = controller.complete('', 4, line='start ')
  185. self.assertEqual(result, None)
  186. def test_complete_start_no_colon(self):
  187. options = DummyClientOptions()
  188. controller = self._makeOne(options)
  189. controller.stdout=StringIO()
  190. controller.vocab = ['help', 'start']
  191. result = controller.complete('f', 0, line='start f')
  192. self.assertEqual(result, 'foo ')
  193. result = controller.complete('f', 1, line='start f')
  194. self.assertEqual(result, None)
  195. def test_complete_start_with_colon(self):
  196. options = DummyClientOptions()
  197. controller = self._makeOne(options)
  198. controller.stdout=StringIO()
  199. controller.vocab = ['help', 'start']
  200. result = controller.complete('foo:', 0, line='start foo:')
  201. self.assertEqual(result, 'foo:foo ')
  202. result = controller.complete('foo:', 1, line='start foo:')
  203. self.assertEqual(result, 'foo:* ')
  204. result = controller.complete('foo:', 2, line='start foo:')
  205. self.assertEqual(result, None)
  206. def test_complete_start_uncompletable(self):
  207. options = DummyClientOptions()
  208. controller = self._makeOne(options)
  209. controller.stdout=StringIO()
  210. controller.vocab = ['help', 'start']
  211. result = controller.complete('bad', 0, line='start bad')
  212. self.assertEqual(result, None)
  213. def test_complete_caches_process_info(self):
  214. options = DummyClientOptions()
  215. controller = self._makeOne(options)
  216. controller.stdout=StringIO()
  217. controller.vocab = ['help', 'start']
  218. result = controller.complete('', 0, line='start ')
  219. self.assertNotEqual(result, None)
  220. def f(*arg, **kw):
  221. raise Exception("should not have called getAllProcessInfo")
  222. controller.options._server.supervisor.getAllProcessInfo = f
  223. controller.complete('', 1, line='start ')
  224. def test_complete_add_empty(self):
  225. options = DummyClientOptions()
  226. controller = self._makeOne(options)
  227. controller.stdout=StringIO()
  228. controller.vocab = ['help', 'add']
  229. result = controller.complete('', 0, line='add ')
  230. self.assertEqual(result, 'foo ')
  231. result = controller.complete('', 1, line='add ')
  232. self.assertEqual(result, 'bar ')
  233. result = controller.complete('', 2, line='add ')
  234. self.assertEqual(result, 'baz ')
  235. result = controller.complete('', 3, line='add ')
  236. self.assertEqual(result, None)
  237. def test_complete_add_uncompletable(self):
  238. options = DummyClientOptions()
  239. controller = self._makeOne(options)
  240. controller.stdout=StringIO()
  241. controller.vocab = ['help', 'add']
  242. result = controller.complete('bad', 0, line='add bad')
  243. self.assertEqual(result, None)
  244. def test_complete_add_group(self):
  245. options = DummyClientOptions()
  246. controller = self._makeOne(options)
  247. controller.stdout=StringIO()
  248. controller.vocab = ['help', 'add']
  249. result = controller.complete('f', 0, line='add f')
  250. self.assertEqual(result, 'foo ')
  251. result = controller.complete('f', 1, line='add f')
  252. self.assertEqual(result, None)
  253. def test_complete_reload_arg_uncompletable(self):
  254. options = DummyClientOptions()
  255. controller = self._makeOne(options)
  256. controller.stdout=StringIO()
  257. controller.vocab = ['help', 'reload']
  258. result = controller.complete('', 1, line='reload ')
  259. self.assertEqual(result, None)
  260. def test_complete_semicolon_separated_commands(self):
  261. options = DummyClientOptions()
  262. controller = self._makeOne(options)
  263. controller.stdout=StringIO()
  264. controller.vocab = ['help', 'start']
  265. result = controller.complete('f', 0, line='help;start f')
  266. self.assertEqual(result, 'foo ')
  267. result = controller.complete('f', 1, line='help;start f')
  268. self.assertEqual(result, None)
  269. def test_nohelp(self):
  270. options = DummyClientOptions()
  271. controller = self._makeOne(options)
  272. self.assertEqual(controller.nohelp, '*** No help on %s')
  273. def test_do_help(self):
  274. options = DummyClientOptions()
  275. controller = self._makeOne(options)
  276. controller.stdout = StringIO()
  277. results = controller.do_help('')
  278. helpval = controller.stdout.getvalue()
  279. self.assertEqual(results, None)
  280. self.assertEqual(helpval, 'foo helped')
  281. def test_do_help_for_help(self):
  282. options = DummyClientOptions()
  283. controller = self._makeOne(options)
  284. controller.stdout = StringIO()
  285. results = controller.do_help("help")
  286. self.assertEqual(results, None)
  287. helpval = controller.stdout.getvalue()
  288. self.assertTrue("help\t\tPrint a list" in helpval)
  289. def test_get_supervisor_returns_serverproxy_supervisor_namespace(self):
  290. options = DummyClientOptions()
  291. controller = self._makeOne(options)
  292. proxy = controller.get_supervisor()
  293. expected = options.getServerProxy().supervisor
  294. self.assertEqual(proxy, expected)
  295. def test_get_server_proxy_with_no_args_returns_serverproxy(self):
  296. options = DummyClientOptions()
  297. controller = self._makeOne(options)
  298. proxy = controller.get_server_proxy()
  299. expected = options.getServerProxy()
  300. self.assertEqual(proxy, expected)
  301. def test_get_server_proxy_with_namespace_returns_that_namespace(self):
  302. options = DummyClientOptions()
  303. controller = self._makeOne(options)
  304. proxy = controller.get_server_proxy('system')
  305. expected = options.getServerProxy().system
  306. self.assertEqual(proxy, expected)
  307. def test_real_controller_initialization(self):
  308. from supervisor.options import ClientOptions
  309. args = [] # simulating starting without parameters
  310. options = ClientOptions()
  311. # No default config file search in case they would exist
  312. self.assertTrue(len(options.searchpaths) > 0)
  313. options.searchpaths = []
  314. options.realize(args, doc=__doc__)
  315. self._makeOne(options) # should not raise
  316. class TestControllerPluginBase(unittest.TestCase):
  317. def _getTargetClass(self):
  318. from supervisor.supervisorctl import ControllerPluginBase
  319. return ControllerPluginBase
  320. def _makeOne(self, *arg, **kw):
  321. klass = self._getTargetClass()
  322. options = DummyClientOptions()
  323. ctl = DummyController(options)
  324. plugin = klass(ctl, *arg, **kw)
  325. return plugin
  326. def test_do_help_noarg(self):
  327. plugin = self._makeOne()
  328. result = plugin.do_help(None)
  329. self.assertEqual(result, None)
  330. self.assertEqual(plugin.ctl.stdout.getvalue(), '\n')
  331. self.assertEqual(len(plugin.ctl.topics_printed), 1)
  332. topics = plugin.ctl.topics_printed[0]
  333. self.assertEqual(topics[0], 'unnamed commands (type help <topic>):')
  334. self.assertEqual(topics[1], [])
  335. self.assertEqual(topics[2], 15)
  336. self.assertEqual(topics[3], 80)
  337. def test_do_help_witharg(self):
  338. plugin = self._makeOne()
  339. result = plugin.do_help('foo')
  340. self.assertEqual(result, None)
  341. self.assertEqual(plugin.ctl.stdout.getvalue(), 'no help on foo\n')
  342. self.assertEqual(len(plugin.ctl.topics_printed), 0)
  343. class TestDefaultControllerPlugin(unittest.TestCase):
  344. def _getTargetClass(self):
  345. from supervisor.supervisorctl import DefaultControllerPlugin
  346. return DefaultControllerPlugin
  347. def _makeOne(self, *arg, **kw):
  348. klass = self._getTargetClass()
  349. options = DummyClientOptions()
  350. ctl = DummyController(options)
  351. plugin = klass(ctl, *arg, **kw)
  352. return plugin
  353. def test_tail_toofewargs(self):
  354. plugin = self._makeOne()
  355. result = plugin.do_tail('')
  356. self.assertEqual(result, None)
  357. lines = plugin.ctl.stdout.getvalue().split('\n')
  358. self.assertEqual(lines[0], 'Error: too few arguments')
  359. def test_tail_toomanyargs(self):
  360. plugin = self._makeOne()
  361. result = plugin.do_tail('one two three four')
  362. self.assertEqual(result, None)
  363. lines = plugin.ctl.stdout.getvalue().split('\n')
  364. self.assertEqual(lines[0], 'Error: too many arguments')
  365. def test_tail_f_noprocname(self):
  366. plugin = self._makeOne()
  367. result = plugin.do_tail('-f')
  368. self.assertEqual(result, None)
  369. lines = plugin.ctl.stdout.getvalue().split('\n')
  370. self.assertEqual(lines[0], 'Error: tail requires process name')
  371. def test_tail_bad_modifier(self):
  372. plugin = self._makeOne()
  373. result = plugin.do_tail('-z foo')
  374. self.assertEqual(result, None)
  375. lines = plugin.ctl.stdout.getvalue().split('\n')
  376. self.assertEqual(lines[0], 'Error: bad argument -z')
  377. def test_tail_defaults(self):
  378. plugin = self._makeOne()
  379. result = plugin.do_tail('foo')
  380. self.assertEqual(result, None)
  381. lines = plugin.ctl.stdout.getvalue().split('\n')
  382. self.assertEqual(len(lines), 12)
  383. self.assertEqual(lines[0], 'output line')
  384. def test_tail_no_file(self):
  385. plugin = self._makeOne()
  386. result = plugin.do_tail('NO_FILE')
  387. self.assertEqual(result, None)
  388. lines = plugin.ctl.stdout.getvalue().split('\n')
  389. self.assertEqual(len(lines), 2)
  390. self.assertEqual(lines[0], 'NO_FILE: ERROR (no log file)')
  391. def test_tail_failed(self):
  392. plugin = self._makeOne()
  393. result = plugin.do_tail('FAILED')
  394. self.assertEqual(result, None)
  395. lines = plugin.ctl.stdout.getvalue().split('\n')
  396. self.assertEqual(len(lines), 2)
  397. self.assertEqual(lines[0], 'FAILED: ERROR (unknown error reading log)')
  398. def test_tail_bad_name(self):
  399. plugin = self._makeOne()
  400. result = plugin.do_tail('BAD_NAME')
  401. self.assertEqual(result, None)
  402. lines = plugin.ctl.stdout.getvalue().split('\n')
  403. self.assertEqual(len(lines), 2)
  404. self.assertEqual(lines[0], 'BAD_NAME: ERROR (no such process name)')
  405. def test_tail_bytesmodifier(self):
  406. plugin = self._makeOne()
  407. result = plugin.do_tail('-10 foo')
  408. self.assertEqual(result, None)
  409. lines = plugin.ctl.stdout.getvalue().split('\n')
  410. self.assertEqual(len(lines), 3)
  411. self.assertEqual(lines[0], 'tput line')
  412. def test_tail_explicit_channel_stdout_nomodifier(self):
  413. plugin = self._makeOne()
  414. result = plugin.do_tail('foo stdout')
  415. self.assertEqual(result, None)
  416. lines = plugin.ctl.stdout.getvalue().split('\n')
  417. self.assertEqual(len(lines), 12)
  418. self.assertEqual(lines[0], 'output line')
  419. def test_tail_explicit_channel_stderr_nomodifier(self):
  420. plugin = self._makeOne()
  421. result = plugin.do_tail('foo stderr')
  422. self.assertEqual(result, None)
  423. lines = plugin.ctl.stdout.getvalue().split('\n')
  424. self.assertEqual(len(lines), 12)
  425. self.assertEqual(lines[0], 'output line')
  426. def test_tail_explicit_channel_unrecognized(self):
  427. plugin = self._makeOne()
  428. result = plugin.do_tail('foo fudge')
  429. self.assertEqual(result, None)
  430. value = plugin.ctl.stdout.getvalue().strip()
  431. self.assertEqual(value, "Error: bad channel 'fudge'")
  432. def test_status_help(self):
  433. plugin = self._makeOne()
  434. plugin.help_status()
  435. out = plugin.ctl.stdout.getvalue()
  436. self.assertTrue("status <name>" in out)
  437. def test_status_table_process_column_min_width(self):
  438. plugin = self._makeOne()
  439. result = plugin.do_status('')
  440. self.assertEqual(result, None)
  441. lines = plugin.ctl.stdout.getvalue().split("\n")
  442. self.assertEqual(lines[0].index("RUNNING"), 33)
  443. def test_status_table_process_column_expands(self):
  444. plugin = self._makeOne()
  445. options = plugin.ctl.options
  446. def f(*arg, **kw):
  447. from supervisor.states import ProcessStates
  448. return [{'name': 'foo'*50, # long name
  449. 'group':'foo',
  450. 'pid': 11,
  451. 'state': ProcessStates.RUNNING,
  452. 'statename': 'RUNNING',
  453. 'start': 0,
  454. 'stop': 0,
  455. 'spawnerr': '',
  456. 'now': 0,
  457. 'description':'foo description'},
  458. {
  459. 'name': 'bar', # short name
  460. 'group': 'bar',
  461. 'pid': 12,
  462. 'state': ProcessStates.FATAL,
  463. 'statename': 'RUNNING',
  464. 'start': 0,
  465. 'stop': 0,
  466. 'spawnerr': '',
  467. 'now': 0,
  468. 'description': 'bar description',
  469. }]
  470. options._server.supervisor.getAllProcessInfo = f
  471. self.assertEqual(plugin.do_status(''), None)
  472. lines = plugin.ctl.stdout.getvalue().split("\n")
  473. self.assertEqual(lines[0].index("RUNNING"), 157)
  474. self.assertEqual(lines[1].index("RUNNING"), 157)
  475. def test_status_all_processes_no_arg(self):
  476. plugin = self._makeOne()
  477. result = plugin.do_status('')
  478. self.assertEqual(result, None)
  479. value = plugin.ctl.stdout.getvalue().split('\n')
  480. self.assertEqual(value[0].split(None, 2),
  481. ['foo', 'RUNNING', 'foo description'])
  482. self.assertEqual(value[1].split(None, 2),
  483. ['bar', 'FATAL', 'bar description'])
  484. self.assertEqual(value[2].split(None, 2),
  485. ['baz:baz_01', 'STOPPED', 'baz description'])
  486. def test_status_all_processes_all_arg(self):
  487. plugin = self._makeOne()
  488. result = plugin.do_status('all')
  489. self.assertEqual(result, None)
  490. value = plugin.ctl.stdout.getvalue().split('\n')
  491. self.assertEqual(value[0].split(None, 2),
  492. ['foo', 'RUNNING', 'foo description'])
  493. self.assertEqual(value[1].split(None, 2),
  494. ['bar', 'FATAL', 'bar description'])
  495. self.assertEqual(value[2].split(None, 2),
  496. ['baz:baz_01', 'STOPPED', 'baz description'])
  497. def test_status_process_name(self):
  498. plugin = self._makeOne()
  499. result = plugin.do_status('foo')
  500. self.assertEqual(result, None)
  501. value = plugin.ctl.stdout.getvalue().strip()
  502. self.assertEqual(value.split(None, 2),
  503. ['foo', 'RUNNING', 'foo description'])
  504. def test_status_group_name(self):
  505. plugin = self._makeOne()
  506. result = plugin.do_status('baz:*')
  507. self.assertEqual(result, None)
  508. value = plugin.ctl.stdout.getvalue().split('\n')
  509. self.assertEqual(value[0].split(None, 2),
  510. ['baz:baz_01', 'STOPPED', 'baz description'])
  511. def test_status_mixed_names(self):
  512. plugin = self._makeOne()
  513. result = plugin.do_status('foo baz:*')
  514. self.assertEqual(result, None)
  515. value = plugin.ctl.stdout.getvalue().split('\n')
  516. self.assertEqual(value[0].split(None, 2),
  517. ['foo', 'RUNNING', 'foo description'])
  518. self.assertEqual(value[1].split(None, 2),
  519. ['baz:baz_01', 'STOPPED', 'baz description'])
  520. def test_status_bad_group_name(self):
  521. plugin = self._makeOne()
  522. result = plugin.do_status('badgroup:*')
  523. self.assertEqual(result, None)
  524. value = plugin.ctl.stdout.getvalue().split('\n')
  525. self.assertEqual(value[0], "badgroup: ERROR (no such group)")
  526. def test_status_bad_process_name(self):
  527. plugin = self._makeOne()
  528. result = plugin.do_status('badprocess')
  529. self.assertEqual(result, None)
  530. value = plugin.ctl.stdout.getvalue().split('\n')
  531. self.assertEqual(value[0], "badprocess: ERROR (no such process)")
  532. def test_status_bad_process_name_with_group(self):
  533. plugin = self._makeOne()
  534. result = plugin.do_status('badgroup:badprocess')
  535. self.assertEqual(result, None)
  536. value = plugin.ctl.stdout.getvalue().split('\n')
  537. self.assertEqual(value[0], "badgroup:badprocess: "
  538. "ERROR (no such process)")
  539. def test_start_help(self):
  540. plugin = self._makeOne()
  541. plugin.help_start()
  542. out = plugin.ctl.stdout.getvalue()
  543. self.assertTrue("start <name>" in out)
  544. def test_start_fail(self):
  545. plugin = self._makeOne()
  546. result = plugin.do_start('')
  547. self.assertEqual(result, None)
  548. expected = "Error: start requires a process name"
  549. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0], expected)
  550. def test_start_badname(self):
  551. plugin = self._makeOne()
  552. result = plugin.do_start('BAD_NAME')
  553. self.assertEqual(result, None)
  554. self.assertEqual(plugin.ctl.stdout.getvalue(),
  555. 'BAD_NAME: ERROR (no such process)\n')
  556. def test_start_no_file(self):
  557. plugin = self._makeOne()
  558. result = plugin.do_start('NO_FILE')
  559. self.assertEqual(result, None)
  560. self.assertEqual(plugin.ctl.stdout.getvalue(),
  561. 'NO_FILE: ERROR (no such file)\n')
  562. def test_start_not_executable(self):
  563. plugin = self._makeOne()
  564. result = plugin.do_start('NOT_EXECUTABLE')
  565. self.assertEqual(result, None)
  566. self.assertEqual(plugin.ctl.stdout.getvalue(),
  567. 'NOT_EXECUTABLE: ERROR (file is not executable)\n')
  568. def test_start_alreadystarted(self):
  569. plugin = self._makeOne()
  570. result = plugin.do_start('ALREADY_STARTED')
  571. self.assertEqual(result, None)
  572. self.assertEqual(plugin.ctl.stdout.getvalue(),
  573. 'ALREADY_STARTED: ERROR (already started)\n')
  574. def test_start_spawnerror(self):
  575. plugin = self._makeOne()
  576. result = plugin.do_start('SPAWN_ERROR')
  577. self.assertEqual(result, None)
  578. self.assertEqual(plugin.ctl.stdout.getvalue(),
  579. 'SPAWN_ERROR: ERROR (spawn error)\n')
  580. def test_start_abnormaltermination(self):
  581. plugin = self._makeOne()
  582. result = plugin.do_start('ABNORMAL_TERMINATION')
  583. self.assertEqual(result, None)
  584. expected = 'ABNORMAL_TERMINATION: ERROR (abnormal termination)\n'
  585. self.assertEqual(plugin.ctl.stdout.getvalue(), expected)
  586. def test_start_one_success(self):
  587. plugin = self._makeOne()
  588. result = plugin.do_start('foo')
  589. self.assertEqual(result, None)
  590. self.assertEqual(plugin.ctl.stdout.getvalue(),
  591. 'foo: started\n')
  592. def test_start_one_with_group_name_success(self):
  593. plugin = self._makeOne()
  594. result = plugin.do_start('foo:foo')
  595. self.assertEqual(result, None)
  596. self.assertEqual(plugin.ctl.stdout.getvalue(),
  597. 'foo: started\n')
  598. def test_start_many(self):
  599. plugin = self._makeOne()
  600. result = plugin.do_start('foo bar')
  601. self.assertEqual(result, None)
  602. self.assertEqual(plugin.ctl.stdout.getvalue(),
  603. 'foo: started\nbar: started\n')
  604. def test_start_group(self):
  605. plugin = self._makeOne()
  606. result = plugin.do_start('foo:')
  607. self.assertEqual(result, None)
  608. self.assertEqual(plugin.ctl.stdout.getvalue(),
  609. 'foo:foo_00: started\n'
  610. 'foo:foo_01: started\n')
  611. def test_start_group_bad_name(self):
  612. plugin = self._makeOne()
  613. result = plugin.do_start('BAD_NAME:')
  614. self.assertEqual(result, None)
  615. self.assertEqual(plugin.ctl.stdout.getvalue(),
  616. 'BAD_NAME: ERROR (no such group)\n')
  617. def test_start_all(self):
  618. plugin = self._makeOne()
  619. result = plugin.do_start('all')
  620. self.assertEqual(result, None)
  621. self.assertEqual(plugin.ctl.stdout.getvalue(),
  622. 'foo: started\n'
  623. 'foo2: started\n'
  624. 'failed_group:failed: ERROR (spawn error)\n')
  625. def test_stop_help(self):
  626. plugin = self._makeOne()
  627. plugin.help_stop()
  628. out = plugin.ctl.stdout.getvalue()
  629. self.assertTrue("stop <name>" in out)
  630. def test_stop_fail(self):
  631. plugin = self._makeOne()
  632. result = plugin.do_stop('')
  633. self.assertEqual(result, None)
  634. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
  635. "Error: stop requires a process name")
  636. def test_stop_badname(self):
  637. plugin = self._makeOne()
  638. result = plugin.do_stop('BAD_NAME')
  639. self.assertEqual(result, None)
  640. self.assertEqual(plugin.ctl.stdout.getvalue(),
  641. 'BAD_NAME: ERROR (no such process)\n')
  642. def test_stop_notrunning(self):
  643. plugin = self._makeOne()
  644. result = plugin.do_stop('NOT_RUNNING')
  645. self.assertEqual(result, None)
  646. self.assertEqual(plugin.ctl.stdout.getvalue(),
  647. 'NOT_RUNNING: ERROR (not running)\n')
  648. def test_stop_failed(self):
  649. plugin = self._makeOne()
  650. result = plugin.do_stop('FAILED')
  651. self.assertEqual(result, None)
  652. self.assertEqual(plugin.ctl.stdout.getvalue(), 'FAILED\n')
  653. def test_stop_one_success(self):
  654. plugin = self._makeOne()
  655. result = plugin.do_stop('foo')
  656. self.assertEqual(result, None)
  657. self.assertEqual(plugin.ctl.stdout.getvalue(),
  658. 'foo: stopped\n')
  659. def test_stop_one_with_group_name_success(self):
  660. plugin = self._makeOne()
  661. result = plugin.do_stop('foo:foo')
  662. self.assertEqual(result, None)
  663. self.assertEqual(plugin.ctl.stdout.getvalue(),
  664. 'foo: stopped\n')
  665. def test_stop_many(self):
  666. plugin = self._makeOne()
  667. result = plugin.do_stop('foo bar')
  668. self.assertEqual(result, None)
  669. self.assertEqual(plugin.ctl.stdout.getvalue(),
  670. 'foo: stopped\n'
  671. 'bar: stopped\n')
  672. def test_stop_group(self):
  673. plugin = self._makeOne()
  674. result = plugin.do_stop('foo:')
  675. self.assertEqual(result, None)
  676. self.assertEqual(plugin.ctl.stdout.getvalue(),
  677. 'foo:foo_00: stopped\n'
  678. 'foo:foo_01: stopped\n')
  679. def test_stop_group_bad_name(self):
  680. plugin = self._makeOne()
  681. result = plugin.do_stop('BAD_NAME:')
  682. self.assertEqual(result, None)
  683. self.assertEqual(plugin.ctl.stdout.getvalue(),
  684. 'BAD_NAME: ERROR (no such group)\n')
  685. def test_stop_all(self):
  686. plugin = self._makeOne()
  687. result = plugin.do_stop('all')
  688. self.assertEqual(result, None)
  689. self.assertEqual(plugin.ctl.stdout.getvalue(),
  690. 'foo: stopped\n'
  691. 'foo2: stopped\n'
  692. 'failed_group:failed: ERROR (no such process)\n')
  693. def test_stop_upcheck_failed(self):
  694. plugin = self._makeOne()
  695. plugin.ctl.upcheck = lambda: False
  696. called = []
  697. def f(*arg, **kw):
  698. called.append(True)
  699. supervisor = plugin.ctl.options._server.supervisor
  700. supervisor.stopAllProcesses = f
  701. supervisor.stopProcessGroup = f
  702. plugin.do_stop('foo')
  703. self.assertEqual(called, [])
  704. def test_signal_help(self):
  705. plugin = self._makeOne()
  706. plugin.help_signal()
  707. out = plugin.ctl.stdout.getvalue()
  708. self.assertTrue("signal <signal name> <name>" in out)
  709. def test_signal_fail_no_arg(self):
  710. plugin = self._makeOne()
  711. result = plugin.do_signal('')
  712. self.assertEqual(result, None)
  713. msg = 'Error: signal requires a signal name and a process name'
  714. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0], msg)
  715. def test_signal_fail_one_arg(self):
  716. plugin = self._makeOne()
  717. result = plugin.do_signal('hup')
  718. self.assertEqual(result, None)
  719. msg = 'Error: signal requires a signal name and a process name'
  720. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0], msg)
  721. def test_signal_bad_signal(self):
  722. plugin = self._makeOne()
  723. result = plugin.do_signal('BAD_SIGNAL foo')
  724. self.assertEqual(result, None)
  725. self.assertEqual(plugin.ctl.stdout.getvalue(),
  726. 'foo: ERROR (bad signal name)\n')
  727. def test_signal_bad_name(self):
  728. plugin = self._makeOne()
  729. result = plugin.do_signal('HUP BAD_NAME')
  730. self.assertEqual(result, None)
  731. self.assertEqual(plugin.ctl.stdout.getvalue(),
  732. 'BAD_NAME: ERROR (no such process)\n')
  733. def test_signal_not_running(self):
  734. plugin = self._makeOne()
  735. result = plugin.do_signal('HUP NOT_RUNNING')
  736. self.assertEqual(result, None)
  737. self.assertEqual(plugin.ctl.stdout.getvalue(),
  738. 'NOT_RUNNING: ERROR (not running)\n')
  739. def test_signal_failed(self):
  740. plugin = self._makeOne()
  741. result = plugin.do_signal('HUP FAILED')
  742. self.assertEqual(result, None)
  743. self.assertEqual(plugin.ctl.stdout.getvalue(), 'FAILED\n')
  744. def test_signal_one_success(self):
  745. plugin = self._makeOne()
  746. result = plugin.do_signal('HUP foo')
  747. self.assertEqual(result, None)
  748. self.assertEqual(plugin.ctl.stdout.getvalue(), 'foo: signalled\n')
  749. def test_signal_many(self):
  750. plugin = self._makeOne()
  751. result = plugin.do_signal('HUP foo bar')
  752. self.assertEqual(result, None)
  753. self.assertEqual(plugin.ctl.stdout.getvalue(),
  754. 'foo: signalled\n'
  755. 'bar: signalled\n')
  756. def test_signal_group(self):
  757. plugin = self._makeOne()
  758. result = plugin.do_signal('HUP foo:')
  759. self.assertEqual(result, None)
  760. self.assertEqual(plugin.ctl.stdout.getvalue(),
  761. 'foo:foo_00: signalled\n'
  762. 'foo:foo_01: signalled\n')
  763. def test_signal_all(self):
  764. plugin = self._makeOne()
  765. result = plugin.do_signal('HUP all')
  766. self.assertEqual(result, None)
  767. self.assertEqual(plugin.ctl.stdout.getvalue(),
  768. 'foo: signalled\n'
  769. 'foo2: signalled\n'
  770. 'failed_group:failed: ERROR (no such process)\n')
  771. def test_restart_help(self):
  772. plugin = self._makeOne()
  773. plugin.help_restart()
  774. out = plugin.ctl.stdout.getvalue()
  775. self.assertTrue("restart <name>" in out)
  776. def test_restart_fail(self):
  777. plugin = self._makeOne()
  778. result = plugin.do_restart('')
  779. self.assertEqual(result, None)
  780. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
  781. 'Error: restart requires a process name')
  782. def test_restart_one(self):
  783. plugin = self._makeOne()
  784. result = plugin.do_restart('foo')
  785. self.assertEqual(result, None)
  786. self.assertEqual(plugin.ctl.stdout.getvalue(),
  787. 'foo: stopped\nfoo: started\n')
  788. def test_restart_all(self):
  789. plugin = self._makeOne()
  790. result = plugin.do_restart('all')
  791. self.assertEqual(result, None)
  792. self.assertEqual(plugin.ctl.stdout.getvalue(),
  793. 'foo: stopped\nfoo2: stopped\n'
  794. 'failed_group:failed: ERROR (no such process)\n'
  795. 'foo: started\nfoo2: started\n'
  796. 'failed_group:failed: ERROR (spawn error)\n')
  797. def test_clear_help(self):
  798. plugin = self._makeOne()
  799. plugin.help_clear()
  800. out = plugin.ctl.stdout.getvalue()
  801. self.assertTrue("clear <name>" in out)
  802. def test_clear_fail(self):
  803. plugin = self._makeOne()
  804. result = plugin.do_clear('')
  805. self.assertEqual(result, None)
  806. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
  807. "Error: clear requires a process name")
  808. def test_clear_badname(self):
  809. plugin = self._makeOne()
  810. result = plugin.do_clear('BAD_NAME')
  811. self.assertEqual(result, None)
  812. self.assertEqual(plugin.ctl.stdout.getvalue(),
  813. 'BAD_NAME: ERROR (no such process)\n')
  814. def test_clear_one_success(self):
  815. plugin = self._makeOne()
  816. result = plugin.do_clear('foo')
  817. self.assertEqual(result, None)
  818. self.assertEqual(plugin.ctl.stdout.getvalue(),
  819. 'foo: cleared\n')
  820. def test_clear_one_with_group_success(self):
  821. plugin = self._makeOne()
  822. result = plugin.do_clear('foo:foo')
  823. self.assertEqual(result, None)
  824. self.assertEqual(plugin.ctl.stdout.getvalue(),
  825. 'foo: cleared\n')
  826. def test_clear_many(self):
  827. plugin = self._makeOne()
  828. result = plugin.do_clear('foo bar')
  829. self.assertEqual(result, None)
  830. self.assertEqual(plugin.ctl.stdout.getvalue(),
  831. 'foo: cleared\nbar: cleared\n')
  832. def test_clear_all(self):
  833. plugin = self._makeOne()
  834. result = plugin.do_clear('all')
  835. self.assertEqual(result, None)
  836. self.assertEqual(plugin.ctl.stdout.getvalue(),
  837. 'foo: cleared\n'
  838. 'foo2: cleared\n'
  839. 'failed_group:failed: ERROR (failed)\n')
  840. def test_open_help(self):
  841. plugin = self._makeOne()
  842. plugin.help_open()
  843. out = plugin.ctl.stdout.getvalue()
  844. self.assertTrue("open <url>" in out)
  845. def test_open_fail(self):
  846. plugin = self._makeOne()
  847. result = plugin.do_open('badname')
  848. self.assertEqual(result, None)
  849. self.assertEqual(plugin.ctl.stdout.getvalue(),
  850. 'ERROR: url must be http:// or unix://\n')
  851. def test_open_succeed(self):
  852. plugin = self._makeOne()
  853. result = plugin.do_open('http://localhost:9002')
  854. self.assertEqual(result, None)
  855. value = plugin.ctl.stdout.getvalue().split('\n')
  856. self.assertEqual(value[0].split(None, 2),
  857. ['foo', 'RUNNING', 'foo description'])
  858. self.assertEqual(value[1].split(None, 2),
  859. ['bar', 'FATAL', 'bar description'])
  860. self.assertEqual(value[2].split(None, 2),
  861. ['baz:baz_01', 'STOPPED', 'baz description'])
  862. def test_version_help(self):
  863. plugin = self._makeOne()
  864. plugin.help_version()
  865. out = plugin.ctl.stdout.getvalue()
  866. self.assertTrue("Show the version of the remote supervisord" in out)
  867. def test_version(self):
  868. plugin = self._makeOne()
  869. plugin.do_version(None)
  870. self.assertEqual(plugin.ctl.stdout.getvalue(), '3000\n')
  871. def test_reload_help(self):
  872. plugin = self._makeOne()
  873. plugin.help_reload()
  874. out = plugin.ctl.stdout.getvalue()
  875. self.assertTrue("Restart the remote supervisord" in out)
  876. def test_reload_fail(self):
  877. plugin = self._makeOne()
  878. options = plugin.ctl.options
  879. options._server.supervisor._restartable = False
  880. result = plugin.do_reload('')
  881. self.assertEqual(result, None)
  882. self.assertEqual(options._server.supervisor._restarted, False)
  883. def test_reload(self):
  884. plugin = self._makeOne()
  885. options = plugin.ctl.options
  886. result = plugin.do_reload('')
  887. self.assertEqual(result, None)
  888. self.assertEqual(options._server.supervisor._restarted, True)
  889. def test_shutdown_help(self):
  890. plugin = self._makeOne()
  891. plugin.help_shutdown()
  892. out = plugin.ctl.stdout.getvalue()
  893. self.assertTrue("Shut the remote supervisord down" in out)
  894. def test_shutdown(self):
  895. plugin = self._makeOne()
  896. options = plugin.ctl.options
  897. result = plugin.do_shutdown('')
  898. self.assertEqual(result, None)
  899. self.assertEqual(options._server.supervisor._shutdown, True)
  900. def test_shutdown_catches_xmlrpc_fault_shutdown_state(self):
  901. plugin = self._makeOne()
  902. from supervisor import xmlrpc
  903. import xmlrpclib
  904. def raise_fault(*arg, **kw):
  905. raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'bye')
  906. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  907. result = plugin.do_shutdown('')
  908. self.assertEqual(result, None)
  909. self.assertEqual(plugin.ctl.stdout.getvalue(),
  910. 'ERROR: already shutting down\n')
  911. def test_shutdown_reraises_other_xmlrpc_faults(self):
  912. plugin = self._makeOne()
  913. from supervisor import xmlrpc
  914. import xmlrpclib
  915. def raise_fault(*arg, **kw):
  916. raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'ouch')
  917. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  918. self.assertRaises(xmlrpclib.Fault,
  919. plugin.do_shutdown, '')
  920. def test_shutdown_catches_socket_error_ECONNREFUSED(self):
  921. plugin = self._makeOne()
  922. import socket
  923. import errno
  924. def raise_fault(*arg, **kw):
  925. raise socket.error(errno.ECONNREFUSED, 'nobody home')
  926. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  927. result = plugin.do_shutdown('')
  928. self.assertEqual(result, None)
  929. output = plugin.ctl.stdout.getvalue()
  930. self.assertTrue('refused connection (already shut down?)' in output)
  931. def test_shutdown_catches_socket_error_ENOENT(self):
  932. plugin = self._makeOne()
  933. import socket
  934. import errno
  935. def raise_fault(*arg, **kw):
  936. raise socket.error(errno.ENOENT, 'no file')
  937. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  938. result = plugin.do_shutdown('')
  939. self.assertEqual(result, None)
  940. output = plugin.ctl.stdout.getvalue()
  941. self.assertTrue('no such file (already shut down?)' in output)
  942. def test_shutdown_reraises_other_socket_errors(self):
  943. plugin = self._makeOne()
  944. import socket
  945. import errno
  946. def raise_fault(*arg, **kw):
  947. raise socket.error(errno.EPERM, 'denied')
  948. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  949. self.assertRaises(socket.error,
  950. plugin.do_shutdown, '')
  951. def test__formatChanges(self):
  952. plugin = self._makeOne()
  953. # Don't explode, plz
  954. plugin._formatChanges([['added'], ['changed'], ['removed']])
  955. plugin._formatChanges([[], [], []])
  956. def test_reread_help(self):
  957. plugin = self._makeOne()
  958. plugin.help_reread()
  959. out = plugin.ctl.stdout.getvalue()
  960. self.assertTrue("Reload the daemon's configuration files" in out)
  961. def test_reread(self):
  962. plugin = self._makeOne()
  963. calls = []
  964. plugin._formatChanges = lambda x: calls.append(x)
  965. result = plugin.do_reread(None)
  966. self.assertEqual(result, None)
  967. self.assertEqual(calls[0], [['added'], ['changed'], ['removed']])
  968. def test_reread_Fault(self):
  969. plugin = self._makeOne()
  970. from supervisor import xmlrpc
  971. import xmlrpclib
  972. def raise_fault(*arg, **kw):
  973. raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'cant')
  974. plugin.ctl.options._server.supervisor.reloadConfig = raise_fault
  975. plugin.do_reread(None)
  976. self.assertEqual(plugin.ctl.stdout.getvalue(),
  977. 'ERROR: cant\n')
  978. def test__formatConfigInfo(self):
  979. info = { 'group': 'group1',
  980. 'name': 'process1',
  981. 'inuse': True,
  982. 'autostart': True,
  983. 'process_prio': 999,
  984. 'group_prio': 999 }
  985. plugin = self._makeOne()
  986. result = plugin._formatConfigInfo(info)
  987. self.assertTrue('in use' in result)
  988. info = { 'group': 'group1',
  989. 'name': 'process1',
  990. 'inuse': False,
  991. 'autostart': False,
  992. 'process_prio': 999,
  993. 'group_prio': 999 }
  994. result = plugin._formatConfigInfo(info)
  995. self.assertTrue('avail' in result)
  996. def test_avail_help(self):
  997. plugin = self._makeOne()
  998. plugin.help_avail()
  999. out = plugin.ctl.stdout.getvalue()
  1000. self.assertTrue("Display all configured" in out)
  1001. def test_avail(self):
  1002. calls = []
  1003. plugin = self._makeOne()
  1004. class FakeSupervisor(object):
  1005. def getAllConfigInfo(self):
  1006. return [{ 'group': 'group1', 'name': 'process1',
  1007. 'inuse': False, 'autostart': False,
  1008. 'process_prio': 999, 'group_prio': 999 }]
  1009. plugin.ctl.get_supervisor = lambda : FakeSupervisor()
  1010. plugin.ctl.output = calls.append
  1011. result = plugin.do_avail('')
  1012. self.assertEqual(result, None)
  1013. def test_add_help(self):
  1014. plugin = self._makeOne()
  1015. plugin.help_add()
  1016. out = plugin.ctl.stdout.getvalue()
  1017. self.assertTrue("add <name>" in out)
  1018. def test_add(self):
  1019. plugin = self._makeOne()
  1020. result = plugin.do_add('foo')
  1021. self.assertEqual(result, None)
  1022. supervisor = plugin.ctl.options._server.supervisor
  1023. self.assertEqual(supervisor.processes, ['foo'])
  1024. def test_add_already_added(self):
  1025. plugin = self._makeOne()
  1026. result = plugin.do_add('ALREADY_ADDED')
  1027. self.assertEqual(result, None)
  1028. self.assertEqual(plugin.ctl.stdout.getvalue(),
  1029. 'ERROR: process group already active\n')
  1030. def test_add_bad_name(self):
  1031. plugin = self._makeOne()
  1032. result = plugin.do_add('BAD_NAME')
  1033. self.assertEqual(result, None)
  1034. self.assertEqual(plugin.ctl.stdout.getvalue(),
  1035. 'ERROR: no such process/group: BAD_NAME\n')
  1036. def test_remove_help(self):
  1037. plugin = self._makeOne()
  1038. plugin.help_remove()
  1039. out = plugin.ctl.stdout.getvalue()
  1040. self.assertTrue("remove <name>" in out)
  1041. def test_remove(self):
  1042. plugin = self._makeOne()
  1043. supervisor = plugin.ctl.options._server.supervisor
  1044. supervisor.processes = ['foo']
  1045. result = plugin.do_remove('foo')
  1046. self.assertEqual(result, None)
  1047. self.assertEqual(supervisor.processes, [])
  1048. def test_remove_bad_name(self):
  1049. plugin = self._makeOne()
  1050. supervisor = plugin.ctl.options._server.supervisor
  1051. supervisor.processes = ['foo']
  1052. result = plugin.do_remove('BAD_NAME')
  1053. self.assertEqual(result, None)
  1054. self.assertEqual(plugin.ctl.stdout.getvalue(),
  1055. 'ERROR: no such process/group: BAD_NAME\n')
  1056. def test_remove_still_running(self):
  1057. plugin = self._makeOne()
  1058. supervisor = plugin.ctl.options._server.supervisor
  1059. supervisor.processes = ['foo']
  1060. result = plugin.do_remove('STILL_RUNNING')
  1061. self.assertEqual(result, None)
  1062. self.assertEqual(plugin.ctl.stdout.getvalue(),
  1063. 'ERROR: process/group still running: STILL_RUNNING\n')
  1064. def test_update_help(self):
  1065. plugin = self._makeOne()
  1066. plugin.help_update()
  1067. out = plugin.ctl.stdout.getvalue()
  1068. self.assertTrue("Reload config and add/remove" in out)
  1069. def test_update_not_on_shutdown(self):
  1070. plugin = self._makeOne()
  1071. supervisor = plugin.ctl.options._server.supervisor
  1072. def reloadConfig():
  1073. from supervisor import xmlrpc
  1074. import xmlrpclib
  1075. raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'blah')
  1076. supervisor.reloadConfig = reloadConfig
  1077. supervisor.processes = ['removed']
  1078. plugin.do_update('')
  1079. self.assertEqual(supervisor.processes, ['removed'])
  1080. def test_update_added_procs(self):
  1081. plugin = self._makeOne()
  1082. supervisor = plugin.ctl.options._server.supervisor
  1083. def reloadConfig():
  1084. return [[['new_proc'], [], []]]
  1085. supervisor.reloadConfig = reloadConfig
  1086. result = plugin.do_update('')
  1087. self.assertEqual(result, None)
  1088. self.assertEqual(supervisor.processes, ['new_proc'])
  1089. def test_update_with_gname(self):
  1090. plugin = self._makeOne()
  1091. supervisor = plugin.ctl.options._server.supervisor
  1092. def reloadConfig():
  1093. return [[['added1', 'added2'], ['changed'], ['removed']]]
  1094. supervisor.reloadConfig = reloadConfig
  1095. supervisor.processes = ['changed', 'removed']
  1096. plugin.do_update('changed')
  1097. self.assertEqual(sorted(supervisor.processes),
  1098. sorted(['changed', 'removed']))
  1099. plugin.do_update('added1 added2')
  1100. self.assertEqual(sorted(supervisor.processes),
  1101. sorted(['changed', 'removed', 'added1', 'added2']))
  1102. plugin.do_update('removed')
  1103. self.assertEqual(sorted(supervisor.processes),
  1104. sorted(['changed', 'added1', 'added2']))
  1105. supervisor.processes = ['changed', 'removed']
  1106. plugin.do_update('removed added1')
  1107. self.assertEqual(sorted(supervisor.processes),
  1108. sorted(['changed', 'added1']))
  1109. supervisor.processes = ['changed', 'removed']
  1110. plugin.do_update('all')
  1111. self.assertEqual(sorted(supervisor.processes),
  1112. sorted(['changed', 'added1', 'added2']))
  1113. def test_update_changed_procs(self):
  1114. from supervisor import xmlrpc
  1115. plugin = self._makeOne()
  1116. supervisor = plugin.ctl.options._server.supervisor
  1117. calls = []
  1118. def reloadConfig():
  1119. return [[[], ['changed_group'], []]]
  1120. supervisor.reloadConfig = reloadConfig
  1121. supervisor.startProcess = lambda x: calls.append(('start', x))
  1122. supervisor.addProcessGroup('changed_group') # fake existence
  1123. results = [{'name': 'changed_process',
  1124. 'group': 'changed_group',
  1125. 'status': xmlrpc.Faults.SUCCESS,
  1126. 'description': 'blah'}]
  1127. def stopProcessGroup(name):
  1128. calls.append(('stop', name))
  1129. return results
  1130. supervisor.stopProcessGroup = stopProcessGroup
  1131. plugin.do_update('')
  1132. self.assertEqual(calls, [('stop', 'changed_group')])
  1133. supervisor.addProcessGroup('changed_group') # fake existence
  1134. calls[:] = []
  1135. results[:] = [{'name': 'changed_process1',
  1136. 'group': 'changed_group',
  1137. 'status': xmlrpc.Faults.NOT_RUNNING,
  1138. 'description': 'blah'},
  1139. {'name': 'changed_process2',
  1140. 'group': 'changed_group',
  1141. 'status': xmlrpc.Faults.FAILED,
  1142. 'description': 'blah'}]
  1143. plugin.do_update('')
  1144. self.assertEqual(calls, [('stop', 'changed_group')])
  1145. supervisor.addProcessGroup('changed_group') # fake existence
  1146. calls[:] = []
  1147. results[:] = [{'name': 'changed_process1',
  1148. 'group': 'changed_group',
  1149. 'status': xmlrpc.Faults.FAILED,
  1150. 'description': 'blah'},
  1151. {'name': 'changed_process2',
  1152. 'group': 'changed_group',
  1153. 'status': xmlrpc.Faults.SUCCESS,
  1154. 'description': 'blah'}]
  1155. plugin.do_update('')
  1156. self.assertEqual(calls, [('stop', 'changed_group')])
  1157. def test_update_removed_procs(self):
  1158. from supervisor import xmlrpc
  1159. plugin = self._makeOne()
  1160. supervisor = plugin.ctl.options._server.supervisor
  1161. def reloadConfig():
  1162. return [[[], [], ['removed_group']]]
  1163. supervisor.reloadConfig = reloadConfig
  1164. results = [{'name': 'removed_process',
  1165. 'group': 'removed_group',
  1166. 'status': xmlrpc.Faults.SUCCESS,
  1167. 'description': 'blah'}]
  1168. supervisor.processes = ['removed_group']
  1169. def stopProcessGroup(name):
  1170. return results
  1171. supervisor.stopProcessGroup = stopProcessGroup
  1172. plugin.do_update('')
  1173. self.assertEqual(supervisor.processes, [])
  1174. results[:] = [{'name': 'removed_process',
  1175. 'group': 'removed_group',
  1176. 'status': xmlrpc.Faults.NOT_RUNNING,
  1177. 'description': 'blah'}]
  1178. supervisor.processes = ['removed_group']
  1179. plugin.do_update('')
  1180. self.assertEqual(supervisor.processes, [])
  1181. results[:] = [{'name': 'removed_process',
  1182. 'group': 'removed_group',
  1183. 'status': xmlrpc.Faults.FAILED,
  1184. 'description': 'blah'}]
  1185. supervisor.processes = ['removed_group']
  1186. plugin.do_update('')
  1187. self.assertEqual(supervisor.processes, ['removed_group'])
  1188. def test_pid_help(self):
  1189. plugin = self._makeOne()
  1190. plugin.help_pid()
  1191. out = plugin.ctl.stdout.getvalue()
  1192. self.assertTrue("pid <name>" in out)
  1193. def test_pid_supervisord(self):
  1194. plugin = self._makeOne()
  1195. result = plugin.do_pid('')
  1196. self.assertEqual(result, None)
  1197. options = plugin.ctl.options
  1198. lines = plugin.ctl.stdout.getvalue().split('\n')
  1199. self.assertEqual(len(lines), 2)
  1200. self.assertEqual(lines[0], str(options._server.supervisor.getPID()))
  1201. def test_pid_allprocesses(self):
  1202. plugin = self._makeOne()
  1203. result = plugin.do_pid('all')
  1204. self.assertEqual(result, None)
  1205. value = plugin.ctl.stdout.getvalue().strip()
  1206. self.assertEqual(value.split(), ['11', '12', '13'])
  1207. def test_pid_badname(self):
  1208. plugin = self._makeOne()
  1209. result = plugin.do_pid('BAD_NAME')
  1210. self.assertEqual(result, None)
  1211. value = plugin.ctl.stdout.getvalue().strip()
  1212. self.assertEqual(value, 'No such process BAD_NAME')
  1213. def test_pid_oneprocess(self):
  1214. plugin = self._makeOne()
  1215. result = plugin.do_pid('foo')
  1216. self.assertEqual(result, None)
  1217. self.assertEqual(plugin.ctl.stdout.getvalue().strip(), '11')
  1218. def test_maintail_help(self):
  1219. plugin = self._makeOne()
  1220. plugin.help_maintail()
  1221. out = plugin.ctl.stdout.getvalue()
  1222. self.assertTrue("tail of supervisor main log file" in out)
  1223. def test_maintail_toomanyargs(self):
  1224. plugin = self._makeOne()
  1225. result = plugin.do_maintail('foo bar')
  1226. self.assertEqual(result, None)
  1227. val = plugin.ctl.stdout.getvalue()
  1228. self.assertTrue(val.startswith('Error: too many'), val)
  1229. def test_maintail_minus_string_fails(self):
  1230. plugin = self._makeOne()
  1231. result = plugin.do_maintail('-wrong')
  1232. self.assertEqual(result, None)
  1233. val = plugin.ctl.stdout.getvalue()
  1234. self.assertTrue(val.startswith('Error: bad argument -wrong'), val)
  1235. def test_maintail_wrong(self):
  1236. plugin = self._makeOne()
  1237. result = plugin.do_maintail('wrong')
  1238. self.assertEqual(result, None)
  1239. val = plugin.ctl.stdout.getvalue()
  1240. self.assertTrue(val.startswith('Error: bad argument wrong'), val)
  1241. def test_maintail_dashf(self):
  1242. plugin = self._makeOne()
  1243. plugin.listener = DummyListener()
  1244. result = plugin.do_maintail('-f')
  1245. self.assertEqual(result, None)
  1246. errors = plugin.listener.errors
  1247. self.assertEqual(len(errors), 1)
  1248. error = errors[0]
  1249. self.assertEqual(plugin.listener.closed,
  1250. 'http://localhost:65532/mainlogtail')
  1251. self.assertEqual(error[0],
  1252. 'http://localhost:65532/mainlogtail')
  1253. for msg in ('Cannot connect', 'socket.error'):
  1254. self.assertTrue(msg in error[1])
  1255. def test_maintail_bad_modifier(self):
  1256. plugin = self._makeOne()
  1257. result = plugin.do_maintail('-z')
  1258. self.assertEqual(result, None)
  1259. lines = plugin.ctl.stdout.getvalue().split('\n')
  1260. self.assertEqual(lines[0], 'Error: bad argument -z')
  1261. def test_maintail_nobytes(self):
  1262. plugin = self._makeOne()
  1263. result = plugin.do_maintail('')
  1264. self.assertEqual(result, None)
  1265. self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')
  1266. def test_maintail_dashbytes(self):
  1267. plugin = self._makeOne()
  1268. result = plugin.do_maintail('-100')
  1269. self.assertEqual(result, None)
  1270. self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')
  1271. def test_maintail_readlog_error_nofile(self):
  1272. plugin = self._makeOne()
  1273. supervisor_rpc = plugin.ctl.get_supervisor()
  1274. from supervisor import xmlrpc
  1275. supervisor_rpc._readlog_error = xmlrpc.Faults.NO_FILE
  1276. result = plugin.do_maintail('-100')
  1277. self.assertEqual(result, None)
  1278. self.assertEqual(plugin.ctl.stdout.getvalue(),
  1279. 'supervisord: ERROR (no log file)\n')
  1280. def test_maintail_readlog_error_failed(self):
  1281. plugin = self._makeOne()
  1282. supervisor_rpc = plugin.ctl.get_supervisor()
  1283. from supervisor import xmlrpc
  1284. supervisor_rpc._readlog_error = xmlrpc.Faults.FAILED
  1285. result = plugin.do_maintail('-100')
  1286. self.assertEqual(result, None)
  1287. self.assertEqual(plugin.ctl.stdout.getvalue(),
  1288. 'supervisord: ERROR (unknown error reading log)\n')
  1289. def test_fg_help(self):
  1290. plugin = self._makeOne()
  1291. plugin.help_fg()
  1292. out = plugin.ctl.stdout.getvalue()
  1293. self.assertTrue("fg <process>" in out)
  1294. def test_fg_too_few_args(self):
  1295. plugin = self._makeOne()
  1296. result = plugin.do_fg('')
  1297. self.assertEqual(result, None)
  1298. lines = plugin.ctl.stdout.getvalue().split('\n')
  1299. self.assertEqual(lines[0], 'Error: no process name supplied')
  1300. def test_fg_too_many_args(self):
  1301. plugin = self._makeOne()
  1302. result = plugin.do_fg('foo bar')
  1303. self.assertEqual(result, None)
  1304. line = plugin.ctl.stdout.getvalue()
  1305. self.assertEqual(line, 'Error: too many process names supplied\n')
  1306. def test_fg_badprocname(self):
  1307. plugin = self._makeOne()
  1308. result = plugin.do_fg('BAD_NAME')
  1309. self.assertEqual(result, None)
  1310. line = plugin.ctl.stdout.getvalue()
  1311. self.assertEqual(line, 'Error: bad process name supplied\n')
  1312. def test_fg_procnotrunning(self):
  1313. plugin = self._makeOne()
  1314. result = plugin.do_fg('bar')
  1315. self.assertEqual(result, None)
  1316. line = plugin.ctl.stdout.getvalue()
  1317. self.assertEqual(line, 'Error: process not running\n')
  1318. result = plugin.do_fg('baz_01')
  1319. lines = plugin.ctl.stdout.getvalue().split('\n')
  1320. self.assertEqual(result, None)
  1321. self.assertEqual(lines[-2], 'Error: process not running')
  1322. def test_exit_help(self):
  1323. plugin = self._makeOne()
  1324. plugin.help_exit()
  1325. out = plugin.ctl.stdout.getvalue()
  1326. self.assertTrue("Exit the supervisor shell" in out)
  1327. def test_quit_help(self):
  1328. plugin = self._makeOne()
  1329. plugin.help_quit()
  1330. out = plugin.ctl.stdout.getvalue()
  1331. self.assertTrue("Exit the supervisor shell" in out)
  1332. class DummyListener:
  1333. def __init__(self):
  1334. self.errors = []
  1335. def error(self, url, msg):
  1336. self.errors.append((url, msg))
  1337. def close(self, url):
  1338. self.closed = url
  1339. class DummyPluginFactory:
  1340. def __init__(self, ctl, **kw):
  1341. self.ctl = ctl
  1342. def do_help(self, arg):
  1343. self.ctl.stdout.write('foo helped')
  1344. class DummyClientOptions:
  1345. def __init__(self):
  1346. self.prompt = 'supervisor'
  1347. self.serverurl = 'http://localhost:65532'
  1348. self.username = 'chrism'
  1349. self.password = '123'
  1350. self.history_file = None
  1351. self.plugins = ()
  1352. self._server = DummyRPCServer()
  1353. self.interactive = False
  1354. self.plugin_factories = [('dummy', DummyPluginFactory, {})]
  1355. def getServerProxy(self):
  1356. return self._server
  1357. class DummyController:
  1358. nohelp = 'no help on %s'
  1359. def __init__(self, options):
  1360. self.options = options
  1361. self.topics_printed = []
  1362. self.stdout = StringIO()
  1363. def upcheck(self):
  1364. return True
  1365. def get_supervisor(self):
  1366. return self.get_server_proxy('supervisor')
  1367. def get_server_proxy(self, namespace=None):
  1368. proxy = self.options.getServerProxy()
  1369. if namespace is None:
  1370. return proxy
  1371. else:
  1372. return getattr(proxy, namespace)
  1373. def output(self, data):
  1374. self.stdout.write(data + '\n')
  1375. def print_topics(self, doc_headers, cmds_doc, rows, cols):
  1376. self.topics_printed.append((doc_headers, cmds_doc, rows, cols))
  1377. class DummyPlugin:
  1378. def __init__(self, controller=None):
  1379. self.ctl = controller
  1380. def do_help(self, arg):
  1381. self.helped = True
  1382. def test_suite():
  1383. return unittest.findTestCases(sys.modules[__name__])
  1384. if __name__ == '__main__':
  1385. unittest.main(defaultTest='test_suite')