test_supervisorctl.py 39 KB

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