test_supervisorctl.py 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  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. value = controller.stdout.getvalue()
  28. self.assertEqual(value, 'Sorry, this version of supervisorctl expects '
  29. 'to talk to a server with API version 3.0, but the remote version is '
  30. '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. value = controller.stdout.getvalue()
  43. self.assertEqual(value, 'Sorry, supervisord responded but did not '
  44. 'recognize the supervisor namespace commands that supervisorctl '
  45. 'uses to control it. Please check that the '
  46. '[rpcinterface:supervisor] section is enabled in the '
  47. 'configuration file (see sample.conf).\n')
  48. def test__upcheck_catches_socket_error_ECONNREFUSED(self):
  49. options = DummyClientOptions()
  50. import socket
  51. import errno
  52. def raise_fault(*arg, **kw):
  53. raise socket.error(errno.ECONNREFUSED, 'nobody home')
  54. options._server.supervisor.getVersion = raise_fault
  55. controller = self._makeOne(options)
  56. controller.stdout = StringIO()
  57. result = controller.upcheck()
  58. self.assertEqual(result, False)
  59. output = controller.stdout.getvalue()
  60. self.assertTrue('refused connection' in output)
  61. def test__upcheck_catches_socket_error_ENOENT(self):
  62. options = DummyClientOptions()
  63. import socket
  64. import errno
  65. def raise_fault(*arg, **kw):
  66. raise socket.error(errno.ENOENT, 'nobody home')
  67. options._server.supervisor.getVersion = raise_fault
  68. controller = self._makeOne(options)
  69. controller.stdout = StringIO()
  70. result = controller.upcheck()
  71. self.assertEqual(result, False)
  72. output = controller.stdout.getvalue()
  73. self.assertTrue('no such file' in output)
  74. def test_onecmd(self):
  75. options = DummyClientOptions()
  76. controller = self._makeOne(options)
  77. controller.stdout = StringIO()
  78. plugin = DummyPlugin()
  79. controller.options.plugins = (plugin,)
  80. result = controller.onecmd('help')
  81. self.assertEqual(result, None)
  82. self.assertEqual(plugin.helped, True)
  83. def test_onecmd_multi_colonseparated(self):
  84. options = DummyClientOptions()
  85. controller = self._makeOne(options)
  86. controller.stdout = StringIO()
  87. plugin = DummyPlugin()
  88. controller.options.plugins = (plugin,)
  89. result = controller.onecmd('help; help')
  90. self.assertEqual(result, None)
  91. self.assertEqual(controller.cmdqueue, [' help'])
  92. self.assertEqual(plugin.helped, True)
  93. def test_completionmatches(self):
  94. options = DummyClientOptions()
  95. controller = self._makeOne(options)
  96. controller.stdout=StringIO()
  97. plugin = DummyPlugin()
  98. controller.options.plugin=(plugin,)
  99. for i in ['add','remove']:
  100. result = controller.completionmatches('',i+' ',1)
  101. self.assertEqual(result,['foo ','bar ','baz '])
  102. result = controller.completionmatches('','fg baz:')
  103. self.assertEqual(result,['baz_01 '])
  104. def test_nohelp(self):
  105. options = DummyClientOptions()
  106. controller = self._makeOne(options)
  107. self.assertEqual(controller.nohelp, '*** No help on %s')
  108. def test_do_help(self):
  109. options = DummyClientOptions()
  110. controller = self._makeOne(options)
  111. controller.stdout = StringIO()
  112. results = controller.do_help(None)
  113. helpval = controller.stdout.getvalue()
  114. self.assertEqual(results, None)
  115. self.assertEqual(helpval, 'foo helped')
  116. def test_get_supervisor_returns_serverproxy_supervisor_namespace(self):
  117. options = DummyClientOptions()
  118. controller = self._makeOne(options)
  119. proxy = controller.get_supervisor()
  120. expected = options.getServerProxy().supervisor
  121. self.assertEqual(proxy, expected)
  122. def test_get_server_proxy_with_no_args_returns_serverproxy(self):
  123. options = DummyClientOptions()
  124. controller = self._makeOne(options)
  125. proxy = controller.get_server_proxy()
  126. expected = options.getServerProxy()
  127. self.assertEqual(proxy, expected)
  128. def test_get_server_proxy_with_namespace_returns_that_namespace(self):
  129. options = DummyClientOptions()
  130. controller = self._makeOne(options)
  131. proxy = controller.get_server_proxy('system')
  132. expected = options.getServerProxy().system
  133. self.assertEqual(proxy, expected)
  134. def test_real_controller_initialization(self):
  135. from supervisor.options import ClientOptions
  136. args = [] # simulating starting without parameters
  137. options = ClientOptions()
  138. # No default config file search in case they would exist
  139. self.assertTrue(len(options.searchpaths) > 0)
  140. options.searchpaths = []
  141. options.realize(args, doc=__doc__)
  142. c = self._makeOne(options)
  143. class TestControllerPluginBase(unittest.TestCase):
  144. def _getTargetClass(self):
  145. from supervisor.supervisorctl import ControllerPluginBase
  146. return ControllerPluginBase
  147. def _makeOne(self, *arg, **kw):
  148. klass = self._getTargetClass()
  149. options = DummyClientOptions()
  150. ctl = DummyController(options)
  151. plugin = klass(ctl, *arg, **kw)
  152. return plugin
  153. def test_do_help_noarg(self):
  154. plugin = self._makeOne()
  155. results = plugin.do_help(None)
  156. self.assertEqual(plugin.ctl.stdout.getvalue(), '\n')
  157. self.assertEqual(len(plugin.ctl.topics_printed), 1)
  158. topics = plugin.ctl.topics_printed[0]
  159. self.assertEqual(topics[0], 'unnamed commands (type help <topic>):')
  160. self.assertEqual(topics[1], [])
  161. self.assertEqual(topics[2], 15)
  162. self.assertEqual(topics[3], 80)
  163. self.assertEqual(results, None)
  164. def test_do_help_witharg(self):
  165. plugin = self._makeOne()
  166. results = plugin.do_help('foo')
  167. self.assertEqual(plugin.ctl.stdout.getvalue(), 'no help on foo\n')
  168. self.assertEqual(len(plugin.ctl.topics_printed), 0)
  169. class TestDefaultControllerPlugin(unittest.TestCase):
  170. def _getTargetClass(self):
  171. from supervisor.supervisorctl import DefaultControllerPlugin
  172. return DefaultControllerPlugin
  173. def _makeOne(self, *arg, **kw):
  174. klass = self._getTargetClass()
  175. options = DummyClientOptions()
  176. ctl = DummyController(options)
  177. plugin = klass(ctl, *arg, **kw)
  178. return plugin
  179. def test_tail_toofewargs(self):
  180. plugin = self._makeOne()
  181. result = plugin.do_tail('')
  182. self.assertEqual(result, None)
  183. lines = plugin.ctl.stdout.getvalue().split('\n')
  184. self.assertEqual(lines[0], 'Error: too few arguments')
  185. def test_tail_toomanyargs(self):
  186. plugin = self._makeOne()
  187. result = plugin.do_tail('one two three four')
  188. self.assertEqual(result, None)
  189. lines = plugin.ctl.stdout.getvalue().split('\n')
  190. self.assertEqual(lines[0], 'Error: too many arguments')
  191. def test_tail_f_noprocname(self):
  192. plugin = self._makeOne()
  193. result = plugin.do_tail('-f')
  194. self.assertEqual(result, None)
  195. lines = plugin.ctl.stdout.getvalue().split('\n')
  196. self.assertEqual(lines[0], 'Error: tail requires process name')
  197. def test_tail_defaults(self):
  198. plugin = self._makeOne()
  199. result = plugin.do_tail('foo')
  200. self.assertEqual(result, None)
  201. lines = plugin.ctl.stdout.getvalue().split('\n')
  202. self.assertEqual(len(lines), 12)
  203. self.assertEqual(lines[0], 'output line')
  204. def test_tail_no_file(self):
  205. plugin = self._makeOne()
  206. result = plugin.do_tail('NO_FILE')
  207. lines = plugin.ctl.stdout.getvalue().split('\n')
  208. self.assertEqual(len(lines), 2)
  209. self.assertEqual(lines[0], 'NO_FILE: ERROR (no log file)')
  210. def test_tail_failed(self):
  211. plugin = self._makeOne()
  212. result = plugin.do_tail('FAILED')
  213. lines = plugin.ctl.stdout.getvalue().split('\n')
  214. self.assertEqual(len(lines), 2)
  215. self.assertEqual(lines[0], 'FAILED: ERROR (unknown error reading log)')
  216. def test_tail_bad_name(self):
  217. plugin = self._makeOne()
  218. result = plugin.do_tail('BAD_NAME')
  219. lines = plugin.ctl.stdout.getvalue().split('\n')
  220. self.assertEqual(len(lines), 2)
  221. self.assertEqual(lines[0], 'BAD_NAME: ERROR (no such process name)')
  222. def test_tail_bytesmodifier(self):
  223. plugin = self._makeOne()
  224. result = plugin.do_tail('-10 foo')
  225. self.assertEqual(result, None)
  226. lines = plugin.ctl.stdout.getvalue().split('\n')
  227. self.assertEqual(len(lines), 3)
  228. self.assertEqual(lines[0], 'tput line')
  229. def test_tail_explicit_channel_stdout_nomodifier(self):
  230. plugin = self._makeOne()
  231. result = plugin.do_tail('foo stdout')
  232. self.assertEqual(result, None)
  233. lines = plugin.ctl.stdout.getvalue().split('\n')
  234. self.assertEqual(len(lines), 12)
  235. self.assertEqual(lines[0], 'output line')
  236. def test_tail_explicit_channel_stderr_nomodifier(self):
  237. plugin = self._makeOne()
  238. result = plugin.do_tail('foo stderr')
  239. lines = plugin.ctl.stdout.getvalue().split('\n')
  240. self.assertEqual(len(lines), 12)
  241. self.assertEqual(lines[0], 'output line')
  242. def test_tail_explicit_channel_unrecognized(self):
  243. plugin = self._makeOne()
  244. result = plugin.do_tail('foo fudge')
  245. self.assertEqual(result, None)
  246. value = plugin.ctl.stdout.getvalue().strip()
  247. self.assertEqual(value, "Error: bad channel 'fudge'")
  248. def test_status_all_processes_no_arg(self):
  249. plugin = self._makeOne()
  250. result = plugin.do_status('')
  251. self.assertEqual(result, None)
  252. value = plugin.ctl.stdout.getvalue().split('\n')
  253. self.assertEqual(value[0].split(None, 2),
  254. ['foo', 'RUNNING', 'foo description'])
  255. self.assertEqual(value[1].split(None, 2),
  256. ['bar', 'FATAL', 'bar description'])
  257. self.assertEqual(value[2].split(None, 2),
  258. ['baz:baz_01', 'STOPPED', 'baz description'])
  259. def test_status_all_processes_all_arg(self):
  260. plugin = self._makeOne()
  261. result = plugin.do_status('all')
  262. self.assertEqual(result, None)
  263. value = plugin.ctl.stdout.getvalue().split('\n')
  264. self.assertEqual(value[0].split(None, 2),
  265. ['foo', 'RUNNING', 'foo description'])
  266. self.assertEqual(value[1].split(None, 2),
  267. ['bar', 'FATAL', 'bar description'])
  268. self.assertEqual(value[2].split(None, 2),
  269. ['baz:baz_01', 'STOPPED', 'baz description'])
  270. def test_status_process_name(self):
  271. plugin = self._makeOne()
  272. result = plugin.do_status('foo')
  273. self.assertEqual(result, None)
  274. value = plugin.ctl.stdout.getvalue().strip()
  275. self.assertEqual(value.split(None, 2),
  276. ['foo', 'RUNNING', 'foo description'])
  277. def test_status_group_name(self):
  278. plugin = self._makeOne()
  279. result = plugin.do_status('baz:*')
  280. value = plugin.ctl.stdout.getvalue().split('\n')
  281. self.assertEqual(value[0].split(None, 2),
  282. ['baz:baz_01', 'STOPPED', 'baz description'])
  283. def test_status_mixed_names(self):
  284. plugin = self._makeOne()
  285. result = plugin.do_status('foo baz:*')
  286. value = plugin.ctl.stdout.getvalue().split('\n')
  287. self.assertEqual(value[0].split(None, 2),
  288. ['foo', 'RUNNING', 'foo description'])
  289. self.assertEqual(value[1].split(None, 2),
  290. ['baz:baz_01', 'STOPPED', 'baz description'])
  291. def test_status_bad_group_name(self):
  292. plugin = self._makeOne()
  293. result = plugin.do_status('badgroup:*')
  294. self.assertEqual(result, None)
  295. value = plugin.ctl.stdout.getvalue().split('\n')
  296. self.assertEqual(value[0], "badgroup: ERROR (no such group)")
  297. def test_status_bad_process_name(self):
  298. plugin = self._makeOne()
  299. result = plugin.do_status('badprocess')
  300. self.assertEqual(result, None)
  301. value = plugin.ctl.stdout.getvalue().split('\n')
  302. self.assertEqual(value[0], "badprocess: ERROR (no such process)")
  303. def test_status_bad_process_name_with_group(self):
  304. plugin = self._makeOne()
  305. result = plugin.do_status('badgroup:badprocess')
  306. self.assertEqual(result, None)
  307. value = plugin.ctl.stdout.getvalue().split('\n')
  308. self.assertEqual(value[0], "badgroup:badprocess: "
  309. "ERROR (no such process)")
  310. def test_start_fail(self):
  311. plugin = self._makeOne()
  312. result = plugin.do_start('')
  313. self.assertEqual(result, None)
  314. expected = "Error: start requires a process name"
  315. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0], expected)
  316. def test_start_badname(self):
  317. plugin = self._makeOne()
  318. result = plugin.do_start('BAD_NAME')
  319. self.assertEqual(result, None)
  320. self.assertEqual(plugin.ctl.stdout.getvalue(),
  321. 'BAD_NAME: ERROR (no such process)\n')
  322. def test_start_no_file(self):
  323. plugin = self._makeOne()
  324. result = plugin.do_start('NO_FILE')
  325. self.assertEqual(result, None)
  326. self.assertEqual(plugin.ctl.stdout.getvalue(),
  327. 'NO_FILE: ERROR (no such file)\n')
  328. def test_start_not_executable(self):
  329. plugin = self._makeOne()
  330. result = plugin.do_start('NOT_EXECUTABLE')
  331. self.assertEqual(result, None)
  332. self.assertEqual(plugin.ctl.stdout.getvalue(),
  333. 'NOT_EXECUTABLE: ERROR (file is not executable)\n')
  334. def test_start_alreadystarted(self):
  335. plugin = self._makeOne()
  336. result = plugin.do_start('ALREADY_STARTED')
  337. self.assertEqual(result, None)
  338. self.assertEqual(plugin.ctl.stdout.getvalue(),
  339. 'ALREADY_STARTED: ERROR (already started)\n')
  340. def test_start_spawnerror(self):
  341. plugin = self._makeOne()
  342. result = plugin.do_start('SPAWN_ERROR')
  343. self.assertEqual(result, None)
  344. self.assertEqual(plugin.ctl.stdout.getvalue(),
  345. 'SPAWN_ERROR: ERROR (spawn error)\n')
  346. def test_start_one_success(self):
  347. plugin = self._makeOne()
  348. result = plugin.do_start('foo')
  349. self.assertEqual(result, None)
  350. self.assertEqual(plugin.ctl.stdout.getvalue(), 'foo: started\n')
  351. def test_start_many(self):
  352. plugin = self._makeOne()
  353. result = plugin.do_start('foo bar')
  354. self.assertEqual(result, None)
  355. self.assertEqual(plugin.ctl.stdout.getvalue(),
  356. 'foo: started\nbar: started\n')
  357. def test_start_group(self):
  358. plugin = self._makeOne()
  359. result = plugin.do_start('foo:')
  360. self.assertEqual(result, None)
  361. self.assertEqual(plugin.ctl.stdout.getvalue(),
  362. 'foo_00: started\nfoo_01: started\n')
  363. def test_start_all(self):
  364. plugin = self._makeOne()
  365. result = plugin.do_start('all')
  366. self.assertEqual(result, None)
  367. self.assertEqual(plugin.ctl.stdout.getvalue(),
  368. 'foo: started\nfoo2: started\nfailed: ERROR (spawn error)\n')
  369. def test_stop_fail(self):
  370. plugin = self._makeOne()
  371. result = plugin.do_stop('')
  372. self.assertEqual(result, None)
  373. expected = "Error: stop requires a process name"
  374. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0], expected)
  375. def test_stop_badname(self):
  376. plugin = self._makeOne()
  377. result = plugin.do_stop('BAD_NAME')
  378. self.assertEqual(result, None)
  379. self.assertEqual(plugin.ctl.stdout.getvalue(),
  380. 'BAD_NAME: ERROR (no such process)\n')
  381. def test_stop_notrunning(self):
  382. plugin = self._makeOne()
  383. result = plugin.do_stop('NOT_RUNNING')
  384. self.assertEqual(result, None)
  385. self.assertEqual(plugin.ctl.stdout.getvalue(),
  386. 'NOT_RUNNING: ERROR (not running)\n')
  387. def test_stop_failed(self):
  388. plugin = self._makeOne()
  389. result = plugin.do_stop('FAILED')
  390. self.assertEqual(result, None)
  391. self.assertEqual(plugin.ctl.stdout.getvalue(), 'FAILED\n')
  392. def test_stop_one_success(self):
  393. plugin = self._makeOne()
  394. result = plugin.do_stop('foo')
  395. self.assertEqual(result, None)
  396. self.assertEqual(plugin.ctl.stdout.getvalue(), 'foo: stopped\n')
  397. def test_stop_many(self):
  398. plugin = self._makeOne()
  399. result = plugin.do_stop('foo bar')
  400. self.assertEqual(result, None)
  401. self.assertEqual(plugin.ctl.stdout.getvalue(),
  402. 'foo: stopped\nbar: stopped\n')
  403. def test_stop_group(self):
  404. plugin = self._makeOne()
  405. result = plugin.do_stop('foo:')
  406. self.assertEqual(result, None)
  407. self.assertEqual(plugin.ctl.stdout.getvalue(),
  408. 'foo_00: stopped\nfoo_01: stopped\n')
  409. def test_stop_all(self):
  410. plugin = self._makeOne()
  411. result = plugin.do_stop('all')
  412. self.assertEqual(result, None)
  413. self.assertEqual(plugin.ctl.stdout.getvalue(),
  414. 'foo: stopped\nfoo2: stopped\nfailed: ERROR (no such process)\n')
  415. def test_restart_fail(self):
  416. plugin = self._makeOne()
  417. result = plugin.do_restart('')
  418. self.assertEqual(result, None)
  419. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0],
  420. 'Error: restart requires a process name')
  421. def test_restart_one(self):
  422. plugin = self._makeOne()
  423. result = plugin.do_restart('foo')
  424. self.assertEqual(result, None)
  425. self.assertEqual(plugin.ctl.stdout.getvalue(),
  426. 'foo: stopped\nfoo: started\n')
  427. def test_restart_all(self):
  428. plugin = self._makeOne()
  429. result = plugin.do_restart('all')
  430. self.assertEqual(result, None)
  431. self.assertEqual(plugin.ctl.stdout.getvalue(),
  432. ('foo: stopped\nfoo2: stopped\n'
  433. 'failed: ERROR (no such process)\n'
  434. 'foo: started\nfoo2: started\n'
  435. 'failed: ERROR (spawn error)\n'))
  436. def test_clear_fail(self):
  437. plugin = self._makeOne()
  438. result = plugin.do_clear('')
  439. self.assertEqual(result, None)
  440. expected = "Error: clear requires a process name"
  441. self.assertEqual(plugin.ctl.stdout.getvalue().split('\n')[0], expected)
  442. def test_clear_badname(self):
  443. plugin = self._makeOne()
  444. result = plugin.do_clear('BAD_NAME')
  445. self.assertEqual(result, None)
  446. self.assertEqual(plugin.ctl.stdout.getvalue(),
  447. 'BAD_NAME: ERROR (no such process)\n')
  448. def test_clear_one_success(self):
  449. plugin = self._makeOne()
  450. result = plugin.do_clear('foo')
  451. self.assertEqual(result, None)
  452. self.assertEqual(plugin.ctl.stdout.getvalue(), 'foo: cleared\n')
  453. def test_clear_many(self):
  454. plugin = self._makeOne()
  455. result = plugin.do_clear('foo bar')
  456. self.assertEqual(result, None)
  457. self.assertEqual(plugin.ctl.stdout.getvalue(),
  458. 'foo: cleared\nbar: cleared\n')
  459. def test_clear_all(self):
  460. plugin = self._makeOne()
  461. result = plugin.do_clear('all')
  462. self.assertEqual(result, None)
  463. self.assertEqual(plugin.ctl.stdout.getvalue(),
  464. 'foo: cleared\nfoo2: cleared\nfailed: ERROR (failed)\n')
  465. def test_open_fail(self):
  466. plugin = self._makeOne()
  467. result = plugin.do_open('badname')
  468. self.assertEqual(result, None)
  469. self.assertEqual(plugin.ctl.stdout.getvalue(),
  470. 'ERROR: url must be http:// or unix://\n')
  471. def test_open_succeed(self):
  472. plugin = self._makeOne()
  473. result = plugin.do_open('http://localhost:9002')
  474. self.assertEqual(result, None)
  475. value = plugin.ctl.stdout.getvalue().split('\n')
  476. self.assertEqual(value[0].split(None, 2),
  477. ['foo', 'RUNNING', 'foo description'])
  478. self.assertEqual(value[1].split(None, 2),
  479. ['bar', 'FATAL', 'bar description'])
  480. self.assertEqual(value[2].split(None, 2),
  481. ['baz:baz_01', 'STOPPED', 'baz description'])
  482. def test_version(self):
  483. plugin = self._makeOne()
  484. plugin.do_version(None)
  485. self.assertEqual(plugin.ctl.stdout.getvalue(), '3000\n')
  486. def test_reload_fail(self):
  487. plugin = self._makeOne()
  488. options = plugin.ctl.options
  489. options._server.supervisor._restartable = False
  490. result = plugin.do_reload('')
  491. self.assertEqual(result, None)
  492. self.assertEqual(options._server.supervisor._restarted, False)
  493. def test_reload(self):
  494. plugin = self._makeOne()
  495. options = plugin.ctl.options
  496. result = plugin.do_reload('')
  497. self.assertEqual(result, None)
  498. self.assertEqual(options._server.supervisor._restarted, True)
  499. def test_shutdown(self):
  500. plugin = self._makeOne()
  501. options = plugin.ctl.options
  502. result = plugin.do_shutdown('')
  503. self.assertEqual(result, None)
  504. self.assertEqual(options._server.supervisor._shutdown, True)
  505. def test_shutdown_catches_xmlrpc_fault_shutdown_state(self):
  506. plugin = self._makeOne()
  507. from supervisor import xmlrpc
  508. import xmlrpclib
  509. def raise_fault(*arg, **kw):
  510. raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'bye')
  511. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  512. result = plugin.do_shutdown('')
  513. self.assertEqual(result, None)
  514. self.assertEqual(plugin.ctl.stdout.getvalue(),
  515. 'ERROR: already shutting down\n')
  516. def test_shutdown_reraises_other_xmlrpc_faults(self):
  517. plugin = self._makeOne()
  518. from supervisor import xmlrpc
  519. import xmlrpclib
  520. def raise_fault(*arg, **kw):
  521. raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'ouch')
  522. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  523. self.assertRaises(xmlrpclib.Fault,
  524. plugin.do_shutdown, '')
  525. def test_shutdown_catches_socket_error_ECONNREFUSED(self):
  526. plugin = self._makeOne()
  527. import socket
  528. import errno
  529. def raise_fault(*arg, **kw):
  530. raise socket.error(errno.ECONNREFUSED, 'nobody home')
  531. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  532. result = plugin.do_shutdown('')
  533. self.assertEqual(result, None)
  534. output = plugin.ctl.stdout.getvalue()
  535. self.assertTrue('refused connection (already shut down?)' in output)
  536. def test_shutdown_catches_socket_error_ENOENT(self):
  537. plugin = self._makeOne()
  538. import socket
  539. import errno
  540. def raise_fault(*arg, **kw):
  541. raise socket.error(errno.ENOENT, 'no file')
  542. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  543. result = plugin.do_shutdown('')
  544. self.assertEqual(result, None)
  545. output = plugin.ctl.stdout.getvalue()
  546. self.assertTrue('no such file (already shut down?)' in output)
  547. def test_shutdown_reraises_other_socket_errors(self):
  548. plugin = self._makeOne()
  549. import socket
  550. import errno
  551. def raise_fault(*arg, **kw):
  552. raise socket.error(errno.EPERM, 'denied')
  553. plugin.ctl.options._server.supervisor.shutdown = raise_fault
  554. self.assertRaises(socket.error,
  555. plugin.do_shutdown, '')
  556. def test__formatChanges(self):
  557. plugin = self._makeOne()
  558. # Don't explode, plz
  559. plugin._formatChanges([['added'], ['changed'], ['removed']])
  560. plugin._formatChanges([[], [], []])
  561. def test_reread(self):
  562. plugin = self._makeOne()
  563. calls = []
  564. plugin._formatChanges = lambda x: calls.append(x)
  565. result = plugin.do_reread(None)
  566. self.assertEqual(result, None)
  567. self.assertEqual(calls[0], [['added'], ['changed'], ['removed']])
  568. def test_reread_Fault(self):
  569. plugin = self._makeOne()
  570. from supervisor import xmlrpc
  571. import xmlrpclib
  572. def raise_fault(*arg, **kw):
  573. raise xmlrpclib.Fault(xmlrpc.Faults.CANT_REREAD, 'cant')
  574. plugin.ctl.options._server.supervisor.reloadConfig = raise_fault
  575. plugin.do_reread(None)
  576. self.assertEqual(plugin.ctl.stdout.getvalue(),
  577. 'ERROR: cant\n')
  578. def test__formatConfigInfo(self):
  579. info = { 'group': 'group1',
  580. 'name': 'process1',
  581. 'inuse': True,
  582. 'autostart': True,
  583. 'process_prio': 999,
  584. 'group_prio': 999 }
  585. plugin = self._makeOne()
  586. result = plugin._formatConfigInfo(info)
  587. self.assertTrue('in use' in result)
  588. info = { 'group': 'group1',
  589. 'name': 'process1',
  590. 'inuse': False,
  591. 'autostart': False,
  592. 'process_prio': 999,
  593. 'group_prio': 999 }
  594. result = plugin._formatConfigInfo(info)
  595. self.assertTrue('avail' in result)
  596. def test_avail(self):
  597. calls = []
  598. plugin = self._makeOne()
  599. class FakeSupervisor(object):
  600. def getAllConfigInfo(self):
  601. return [{ 'group': 'group1', 'name': 'process1',
  602. 'inuse': False, 'autostart': False,
  603. 'process_prio': 999, 'group_prio': 999 }]
  604. plugin.ctl.get_supervisor = lambda : FakeSupervisor()
  605. plugin.ctl.output = calls.append
  606. result = plugin.do_avail('')
  607. self.assertEqual(result, None)
  608. def test_add(self):
  609. plugin = self._makeOne()
  610. result = plugin.do_add('foo')
  611. self.assertEqual(result, None)
  612. supervisor = plugin.ctl.options._server.supervisor
  613. self.assertEqual(supervisor.processes, ['foo'])
  614. def test_add_already_added(self):
  615. plugin = self._makeOne()
  616. result = plugin.do_add('ALREADY_ADDED')
  617. self.assertEqual(result, None)
  618. supervisor = plugin.ctl.options._server.supervisor
  619. self.assertEqual(plugin.ctl.stdout.getvalue(),
  620. 'ERROR: process group already active\n')
  621. def test_add_bad_name(self):
  622. plugin = self._makeOne()
  623. result = plugin.do_add('BAD_NAME')
  624. self.assertEqual(result, None)
  625. supervisor = plugin.ctl.options._server.supervisor
  626. self.assertEqual(plugin.ctl.stdout.getvalue(),
  627. 'ERROR: no such process/group: BAD_NAME\n')
  628. def test_remove(self):
  629. plugin = self._makeOne()
  630. supervisor = plugin.ctl.options._server.supervisor
  631. supervisor.processes = ['foo']
  632. result = plugin.do_remove('foo')
  633. self.assertEqual(result, None)
  634. self.assertEqual(supervisor.processes, [])
  635. def test_remove_bad_name(self):
  636. plugin = self._makeOne()
  637. supervisor = plugin.ctl.options._server.supervisor
  638. supervisor.processes = ['foo']
  639. result = plugin.do_remove('BAD_NAME')
  640. self.assertEqual(result, None)
  641. self.assertEqual(plugin.ctl.stdout.getvalue(),
  642. 'ERROR: no such process/group: BAD_NAME\n')
  643. def test_remove_still_running(self):
  644. plugin = self._makeOne()
  645. supervisor = plugin.ctl.options._server.supervisor
  646. supervisor.processes = ['foo']
  647. result = plugin.do_remove('STILL_RUNNING')
  648. self.assertEqual(result, None)
  649. self.assertEqual(plugin.ctl.stdout.getvalue(),
  650. 'ERROR: process/group still running: STILL_RUNNING\n')
  651. def test_update_not_on_shutdown(self):
  652. plugin = self._makeOne()
  653. supervisor = plugin.ctl.options._server.supervisor
  654. def reloadConfig():
  655. from supervisor import xmlrpc
  656. import xmlrpclib
  657. raise xmlrpclib.Fault(xmlrpc.Faults.SHUTDOWN_STATE, 'blah')
  658. supervisor.reloadConfig = reloadConfig
  659. supervisor.processes = ['removed']
  660. plugin.do_update('')
  661. self.assertEqual(supervisor.processes, ['removed'])
  662. def test_update_added_procs(self):
  663. plugin = self._makeOne()
  664. supervisor = plugin.ctl.options._server.supervisor
  665. calls = []
  666. def reloadConfig():
  667. return [[['new_proc'], [], []]]
  668. supervisor.reloadConfig = reloadConfig
  669. plugin.do_update('')
  670. self.assertEqual(supervisor.processes, ['new_proc'])
  671. def test_update_with_gname(self):
  672. plugin = self._makeOne()
  673. supervisor = plugin.ctl.options._server.supervisor
  674. def reloadConfig():
  675. return [[['added1', 'added2'], ['changed'], ['removed']]]
  676. supervisor.reloadConfig = reloadConfig
  677. supervisor.processes = ['changed', 'removed']
  678. plugin.do_update('changed')
  679. self.assertEqual(sorted(supervisor.processes),
  680. sorted(['changed', 'removed']))
  681. plugin.do_update('added1 added2')
  682. self.assertEqual(sorted(supervisor.processes),
  683. sorted(['changed', 'removed', 'added1', 'added2']))
  684. plugin.do_update('removed')
  685. self.assertEqual(sorted(supervisor.processes),
  686. sorted(['changed', 'added1', 'added2']))
  687. supervisor.processes = ['changed', 'removed']
  688. plugin.do_update('removed added1')
  689. self.assertEqual(sorted(supervisor.processes),
  690. sorted(['changed', 'added1']))
  691. supervisor.processes = ['changed', 'removed']
  692. plugin.do_update('all')
  693. self.assertEqual(sorted(supervisor.processes),
  694. sorted(['changed', 'added1', 'added2']))
  695. def test_update_changed_procs(self):
  696. from supervisor import xmlrpc
  697. plugin = self._makeOne()
  698. supervisor = plugin.ctl.options._server.supervisor
  699. calls = []
  700. def reloadConfig():
  701. return [[[], ['changed_group'], []]]
  702. supervisor.reloadConfig = reloadConfig
  703. supervisor.startProcess = lambda x: calls.append(('start', x))
  704. supervisor.addProcessGroup('changed_group') # fake existence
  705. results = [{'name': 'changed_process',
  706. 'group': 'changed_group',
  707. 'status': xmlrpc.Faults.SUCCESS,
  708. 'description': 'blah'}]
  709. def stopProcessGroup(name):
  710. calls.append(('stop', name))
  711. return results
  712. supervisor.stopProcessGroup = stopProcessGroup
  713. plugin.do_update('')
  714. self.assertEqual(calls, [('stop', 'changed_group')])
  715. supervisor.addProcessGroup('changed_group') # fake existence
  716. calls[:] = []
  717. results[:] = [{'name': 'changed_process1',
  718. 'group': 'changed_group',
  719. 'status': xmlrpc.Faults.NOT_RUNNING,
  720. 'description': 'blah'},
  721. {'name': 'changed_process2',
  722. 'group': 'changed_group',
  723. 'status': xmlrpc.Faults.FAILED,
  724. 'description': 'blah'}]
  725. plugin.do_update('')
  726. self.assertEqual(calls, [('stop', 'changed_group')])
  727. supervisor.addProcessGroup('changed_group') # fake existence
  728. calls[:] = []
  729. results[:] = [{'name': 'changed_process1',
  730. 'group': 'changed_group',
  731. 'status': xmlrpc.Faults.FAILED,
  732. 'description': 'blah'},
  733. {'name': 'changed_process2',
  734. 'group': 'changed_group',
  735. 'status': xmlrpc.Faults.SUCCESS,
  736. 'description': 'blah'}]
  737. plugin.do_update('')
  738. self.assertEqual(calls, [('stop', 'changed_group')])
  739. def test_update_removed_procs(self):
  740. from supervisor import xmlrpc
  741. plugin = self._makeOne()
  742. supervisor = plugin.ctl.options._server.supervisor
  743. def reloadConfig():
  744. return [[[], [], ['removed_group']]]
  745. supervisor.reloadConfig = reloadConfig
  746. results = [{'name': 'removed_process',
  747. 'group': 'removed_group',
  748. 'status': xmlrpc.Faults.SUCCESS,
  749. 'description': 'blah'}]
  750. supervisor.processes = ['removed_group']
  751. def stopProcessGroup(name):
  752. return results
  753. supervisor.stopProcessGroup = stopProcessGroup
  754. plugin.do_update('')
  755. self.assertEqual(supervisor.processes, [])
  756. results[:] = [{'name': 'removed_process',
  757. 'group': 'removed_group',
  758. 'status': xmlrpc.Faults.NOT_RUNNING,
  759. 'description': 'blah'}]
  760. supervisor.processes = ['removed_group']
  761. plugin.do_update('')
  762. self.assertEqual(supervisor.processes, [])
  763. results[:] = [{'name': 'removed_process',
  764. 'group': 'removed_group',
  765. 'status': xmlrpc.Faults.FAILED,
  766. 'description': 'blah'}]
  767. supervisor.processes = ['removed_group']
  768. plugin.do_update('')
  769. self.assertEqual(supervisor.processes, ['removed_group'])
  770. def test_pid_supervisord(self):
  771. plugin = self._makeOne()
  772. result = plugin.do_pid('')
  773. options = plugin.ctl.options
  774. self.assertEqual(result, None)
  775. lines = plugin.ctl.stdout.getvalue().split('\n')
  776. self.assertEqual(len(lines), 2)
  777. self.assertEqual(lines[0], str(options._server.supervisor.getPID()))
  778. def test_pid_allprocesses(self):
  779. plugin = self._makeOne()
  780. result = plugin.do_pid('all')
  781. self.assertEqual(result, None)
  782. value = plugin.ctl.stdout.getvalue().strip()
  783. self.assertEqual(value.split(), ['11', '12', '13'])
  784. def test_pid_badname(self):
  785. plugin = self._makeOne()
  786. result = plugin.do_pid('BAD_NAME')
  787. self.assertEqual(result, None)
  788. value = plugin.ctl.stdout.getvalue().strip()
  789. self.assertEqual(value, 'No such process BAD_NAME')
  790. def test_pid_oneprocess(self):
  791. plugin = self._makeOne()
  792. result = plugin.do_pid('foo')
  793. self.assertEqual(plugin.ctl.stdout.getvalue().strip(), '11')
  794. def test_maintail_toomanyargs(self):
  795. plugin = self._makeOne()
  796. result = plugin.do_maintail('foo bar')
  797. val = plugin.ctl.stdout.getvalue()
  798. self.assertTrue(val.startswith('Error: too many'), val)
  799. def test_maintail_minus_string_fails(self):
  800. plugin = self._makeOne()
  801. result = plugin.do_maintail('-wrong')
  802. val = plugin.ctl.stdout.getvalue()
  803. self.assertTrue(val.startswith('Error: bad argument -wrong'), val)
  804. def test_maintail_wrong(self):
  805. plugin = self._makeOne()
  806. result = plugin.do_maintail('wrong')
  807. val = plugin.ctl.stdout.getvalue()
  808. self.assertTrue(val.startswith('Error: bad argument wrong'), val)
  809. def test_maintail_dashf(self):
  810. plugin = self._makeOne()
  811. plugin.listener = DummyListener()
  812. result = plugin.do_maintail('-f')
  813. errors = plugin.listener.errors
  814. self.assertEqual(len(errors), 1)
  815. error = errors[0]
  816. self.assertEqual(plugin.listener.closed,
  817. 'http://localhost:65532/mainlogtail')
  818. self.assertEqual(error[0],
  819. 'http://localhost:65532/mainlogtail')
  820. for msg in ('Cannot connect', 'socket.error'):
  821. self.assertTrue(msg in error[1])
  822. def test_maintail_nobytes(self):
  823. plugin = self._makeOne()
  824. result = plugin.do_maintail('')
  825. self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')
  826. def test_maintail_dashbytes(self):
  827. plugin = self._makeOne()
  828. result = plugin.do_maintail('-100')
  829. self.assertEqual(plugin.ctl.stdout.getvalue(), 'mainlogdata\n')
  830. def test_maintail_readlog_error_nofile(self):
  831. plugin = self._makeOne()
  832. supervisor_rpc = plugin.ctl.get_supervisor()
  833. from supervisor import xmlrpc
  834. supervisor_rpc._readlog_error = xmlrpc.Faults.NO_FILE
  835. result = plugin.do_maintail('-100')
  836. self.assertEqual(plugin.ctl.stdout.getvalue(),
  837. 'supervisord: ERROR (no log file)\n')
  838. def test_maintail_readlog_error_failed(self):
  839. plugin = self._makeOne()
  840. supervisor_rpc = plugin.ctl.get_supervisor()
  841. from supervisor import xmlrpc
  842. supervisor_rpc._readlog_error = xmlrpc.Faults.FAILED
  843. result = plugin.do_maintail('-100')
  844. self.assertEqual(plugin.ctl.stdout.getvalue(),
  845. 'supervisord: ERROR (unknown error reading log)\n')
  846. def test_fg_too_few_args(self):
  847. plugin = self._makeOne()
  848. result = plugin.do_fg('')
  849. lines = plugin.ctl.stdout.getvalue().split('\n')
  850. self.assertEqual(result, None)
  851. self.assertEqual(lines[0], 'Error: no process name supplied')
  852. def test_fg_too_many_args(self):
  853. plugin = self._makeOne()
  854. result = plugin.do_fg('foo bar')
  855. line = plugin.ctl.stdout.getvalue()
  856. self.assertEqual(result, None)
  857. self.assertEqual(line, 'Error: too many process names supplied\n')
  858. def test_fg_badprocname(self):
  859. plugin = self._makeOne()
  860. result = plugin.do_fg('BAD_NAME')
  861. line = plugin.ctl.stdout.getvalue()
  862. self.assertEqual(result, None)
  863. self.assertEqual(line, 'Error: bad process name supplied\n')
  864. def test_fg_procnotrunning(self):
  865. plugin = self._makeOne()
  866. result = plugin.do_fg('bar')
  867. line = plugin.ctl.stdout.getvalue()
  868. self.assertEqual(result, None)
  869. self.assertEqual(line, 'Error: process not running\n')
  870. result = plugin.do_fg('baz_01')
  871. lines = plugin.ctl.stdout.getvalue().split('\n')
  872. self.assertEqual(result, None)
  873. self.assertEqual(lines[-2], 'Error: process not running')
  874. class DummyListener:
  875. def __init__(self):
  876. self.errors = []
  877. def error(self, url, msg):
  878. self.errors.append((url, msg))
  879. def close(self, url):
  880. self.closed = url
  881. class DummyPluginFactory:
  882. def __init__(self, ctl, **kw):
  883. self.ctl = ctl
  884. def do_help(self, arg):
  885. self.ctl.stdout.write('foo helped')
  886. class DummyClientOptions:
  887. def __init__(self):
  888. self.prompt = 'supervisor'
  889. self.serverurl = 'http://localhost:65532'
  890. self.username = 'chrism'
  891. self.password = '123'
  892. self.history_file = None
  893. self.plugins = ()
  894. self._server = DummyRPCServer()
  895. self.interactive = False
  896. self.plugin_factories = [('dummy', DummyPluginFactory, {})]
  897. def getServerProxy(self):
  898. return self._server
  899. class DummyController:
  900. nohelp = 'no help on %s'
  901. def __init__(self, options):
  902. self.options = options
  903. self.topics_printed = []
  904. self.stdout = StringIO()
  905. def upcheck(self):
  906. return True
  907. def get_supervisor(self):
  908. return self.get_server_proxy('supervisor')
  909. def get_server_proxy(self, namespace=None):
  910. proxy = self.options.getServerProxy()
  911. if namespace is None:
  912. return proxy
  913. else:
  914. return getattr(proxy, namespace)
  915. def output(self, data):
  916. self.stdout.write(data + '\n')
  917. def print_topics(self, doc_headers, cmds_doc, rows, cols):
  918. self.topics_printed.append((doc_headers, cmds_doc, rows, cols))
  919. class DummyPlugin:
  920. def __init__(self, controller=None):
  921. self.ctl = controller
  922. def do_help(self, arg):
  923. self.helped = True
  924. def test_suite():
  925. return unittest.findTestCases(sys.modules[__name__])
  926. if __name__ == '__main__':
  927. unittest.main(defaultTest='test_suite')