test_platforms.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836
  1. from __future__ import absolute_import, unicode_literals
  2. import errno
  3. import os
  4. import pytest
  5. import sys
  6. import signal
  7. import tempfile
  8. from case import Mock, call, mock, patch, skip
  9. from celery import _find_option_with_arg
  10. from celery import platforms
  11. from celery.exceptions import SecurityError
  12. from celery.five import WhateverIO
  13. from celery.platforms import (
  14. get_fdmax,
  15. ignore_errno,
  16. check_privileges,
  17. set_process_title,
  18. set_mp_process_title,
  19. signals,
  20. maybe_drop_privileges,
  21. setuid,
  22. setgid,
  23. initgroups,
  24. parse_uid,
  25. parse_gid,
  26. detached,
  27. DaemonContext,
  28. create_pidlock,
  29. Pidfile,
  30. LockFailed,
  31. setgroups,
  32. _setgroups_hack,
  33. close_open_fds,
  34. fd_by_path,
  35. isatty,
  36. )
  37. try:
  38. import resource
  39. except ImportError: # pragma: no cover
  40. resource = None # noqa
  41. def test_isatty():
  42. fh = Mock(name='fh')
  43. assert isatty(fh) is fh.isatty()
  44. fh.isatty.side_effect = AttributeError()
  45. assert not isatty(fh)
  46. class test_find_option_with_arg:
  47. def test_long_opt(self):
  48. assert _find_option_with_arg(
  49. ['--foo=bar'], long_opts=['--foo']) == 'bar'
  50. def test_short_opt(self):
  51. assert _find_option_with_arg(
  52. ['-f', 'bar'], short_opts=['-f']) == 'bar'
  53. @skip.if_win32()
  54. def test_fd_by_path():
  55. test_file = tempfile.NamedTemporaryFile()
  56. try:
  57. keep = fd_by_path([test_file.name])
  58. assert keep == [test_file.file.fileno()]
  59. with patch('os.open') as _open:
  60. _open.side_effect = OSError()
  61. assert not fd_by_path([test_file.name])
  62. finally:
  63. test_file.close()
  64. def test_close_open_fds(patching):
  65. _close = patching('os.close')
  66. fdmax = patching('celery.platforms.get_fdmax')
  67. with patch('os.closerange', create=True) as closerange:
  68. fdmax.return_value = 3
  69. close_open_fds()
  70. if not closerange.called:
  71. _close.assert_has_calls([call(2), call(1), call(0)])
  72. _close.side_effect = OSError()
  73. _close.side_effect.errno = errno.EBADF
  74. close_open_fds()
  75. class test_ignore_errno:
  76. def test_raises_EBADF(self):
  77. with ignore_errno('EBADF'):
  78. exc = OSError()
  79. exc.errno = errno.EBADF
  80. raise exc
  81. def test_otherwise(self):
  82. with pytest.raises(OSError):
  83. with ignore_errno('EBADF'):
  84. exc = OSError()
  85. exc.errno = errno.ENOENT
  86. raise exc
  87. class test_set_process_title:
  88. def test_no_setps(self):
  89. prev, platforms._setproctitle = platforms._setproctitle, None
  90. try:
  91. set_process_title('foo')
  92. finally:
  93. platforms._setproctitle = prev
  94. @patch('celery.platforms.set_process_title')
  95. @patch('celery.platforms.current_process')
  96. def test_mp_no_hostname(self, current_process, set_process_title):
  97. current_process().name = 'Foo'
  98. set_mp_process_title('foo', info='hello')
  99. set_process_title.assert_called_with('foo:Foo', info='hello')
  100. @patch('celery.platforms.set_process_title')
  101. @patch('celery.platforms.current_process')
  102. def test_mp_hostname(self, current_process, set_process_title):
  103. current_process().name = 'Foo'
  104. set_mp_process_title('foo', hostname='a@q.com', info='hello')
  105. set_process_title.assert_called_with('foo: a@q.com:Foo', info='hello')
  106. class test_Signals:
  107. @patch('signal.getsignal')
  108. def test_getitem(self, getsignal):
  109. signals['SIGINT']
  110. getsignal.assert_called_with(signal.SIGINT)
  111. def test_supported(self):
  112. assert signals.supported('INT')
  113. assert not signals.supported('SIGIMAGINARY')
  114. @skip.if_win32()
  115. def test_reset_alarm(self):
  116. with patch('signal.alarm') as _alarm:
  117. signals.reset_alarm()
  118. _alarm.assert_called_with(0)
  119. def test_arm_alarm(self):
  120. if hasattr(signal, 'setitimer'):
  121. with patch('signal.setitimer', create=True) as seti:
  122. signals.arm_alarm(30)
  123. seti.assert_called()
  124. def test_signum(self):
  125. assert signals.signum(13) == 13
  126. assert signals.signum('INT') == signal.SIGINT
  127. assert signals.signum('SIGINT') == signal.SIGINT
  128. with pytest.raises(TypeError):
  129. signals.signum('int')
  130. signals.signum(object())
  131. @patch('signal.signal')
  132. def test_ignore(self, set):
  133. signals.ignore('SIGINT')
  134. set.assert_called_with(signals.signum('INT'), signals.ignored)
  135. signals.ignore('SIGTERM')
  136. set.assert_called_with(signals.signum('TERM'), signals.ignored)
  137. @patch('signal.signal')
  138. def test_reset(self, set):
  139. signals.reset('SIGINT')
  140. set.assert_called_with(signals.signum('INT'), signals.default)
  141. @patch('signal.signal')
  142. def test_setitem(self, set):
  143. def handle(*args):
  144. return args
  145. signals['INT'] = handle
  146. set.assert_called_with(signal.SIGINT, handle)
  147. @patch('signal.signal')
  148. def test_setitem_raises(self, set):
  149. set.side_effect = ValueError()
  150. signals['INT'] = lambda *a: a
  151. @skip.if_win32()
  152. class test_get_fdmax:
  153. @patch('resource.getrlimit')
  154. def test_when_infinity(self, getrlimit):
  155. with patch('os.sysconf') as sysconfig:
  156. sysconfig.side_effect = KeyError()
  157. getrlimit.return_value = [None, resource.RLIM_INFINITY]
  158. default = object()
  159. assert get_fdmax(default) is default
  160. @patch('resource.getrlimit')
  161. def test_when_actual(self, getrlimit):
  162. with patch('os.sysconf') as sysconfig:
  163. sysconfig.side_effect = KeyError()
  164. getrlimit.return_value = [None, 13]
  165. assert get_fdmax(None) == 13
  166. @skip.if_win32()
  167. class test_maybe_drop_privileges:
  168. def test_on_windows(self):
  169. prev, sys.platform = sys.platform, 'win32'
  170. try:
  171. maybe_drop_privileges()
  172. finally:
  173. sys.platform = prev
  174. @patch('os.getegid')
  175. @patch('os.getgid')
  176. @patch('os.geteuid')
  177. @patch('os.getuid')
  178. @patch('celery.platforms.parse_uid')
  179. @patch('celery.platforms.parse_gid')
  180. @patch('pwd.getpwuid')
  181. @patch('celery.platforms.setgid')
  182. @patch('celery.platforms.setuid')
  183. @patch('celery.platforms.initgroups')
  184. def test_with_uid(self, initgroups, setuid, setgid,
  185. getpwuid, parse_gid, parse_uid, getuid, geteuid,
  186. getgid, getegid):
  187. geteuid.return_value = 10
  188. getuid.return_value = 10
  189. class pw_struct(object):
  190. pw_gid = 50001
  191. def raise_on_second_call(*args, **kwargs):
  192. setuid.side_effect = OSError()
  193. setuid.side_effect.errno = errno.EPERM
  194. setuid.side_effect = raise_on_second_call
  195. getpwuid.return_value = pw_struct()
  196. parse_uid.return_value = 5001
  197. parse_gid.return_value = 5001
  198. maybe_drop_privileges(uid='user')
  199. parse_uid.assert_called_with('user')
  200. getpwuid.assert_called_with(5001)
  201. setgid.assert_called_with(50001)
  202. initgroups.assert_called_with(5001, 50001)
  203. setuid.assert_has_calls([call(5001), call(0)])
  204. setuid.side_effect = raise_on_second_call
  205. def to_root_on_second_call(mock, first):
  206. return_value = [first]
  207. def on_first_call(*args, **kwargs):
  208. ret, return_value[0] = return_value[0], 0
  209. return ret
  210. mock.side_effect = on_first_call
  211. to_root_on_second_call(geteuid, 10)
  212. to_root_on_second_call(getuid, 10)
  213. with pytest.raises(SecurityError):
  214. maybe_drop_privileges(uid='user')
  215. getuid.return_value = getuid.side_effect = None
  216. geteuid.return_value = geteuid.side_effect = None
  217. getegid.return_value = 0
  218. getgid.return_value = 0
  219. setuid.side_effect = raise_on_second_call
  220. with pytest.raises(SecurityError):
  221. maybe_drop_privileges(gid='group')
  222. getuid.reset_mock()
  223. geteuid.reset_mock()
  224. setuid.reset_mock()
  225. getuid.side_effect = geteuid.side_effect = None
  226. def raise_on_second_call(*args, **kwargs):
  227. setuid.side_effect = OSError()
  228. setuid.side_effect.errno = errno.ENOENT
  229. setuid.side_effect = raise_on_second_call
  230. with pytest.raises(OSError):
  231. maybe_drop_privileges(uid='user')
  232. @patch('celery.platforms.parse_uid')
  233. @patch('celery.platforms.parse_gid')
  234. @patch('celery.platforms.setgid')
  235. @patch('celery.platforms.setuid')
  236. @patch('celery.platforms.initgroups')
  237. def test_with_guid(self, initgroups, setuid, setgid,
  238. parse_gid, parse_uid):
  239. def raise_on_second_call(*args, **kwargs):
  240. setuid.side_effect = OSError()
  241. setuid.side_effect.errno = errno.EPERM
  242. setuid.side_effect = raise_on_second_call
  243. parse_uid.return_value = 5001
  244. parse_gid.return_value = 50001
  245. maybe_drop_privileges(uid='user', gid='group')
  246. parse_uid.assert_called_with('user')
  247. parse_gid.assert_called_with('group')
  248. setgid.assert_called_with(50001)
  249. initgroups.assert_called_with(5001, 50001)
  250. setuid.assert_has_calls([call(5001), call(0)])
  251. setuid.side_effect = None
  252. with pytest.raises(SecurityError):
  253. maybe_drop_privileges(uid='user', gid='group')
  254. setuid.side_effect = OSError()
  255. setuid.side_effect.errno = errno.EINVAL
  256. with pytest.raises(OSError):
  257. maybe_drop_privileges(uid='user', gid='group')
  258. @patch('celery.platforms.setuid')
  259. @patch('celery.platforms.setgid')
  260. @patch('celery.platforms.parse_gid')
  261. def test_only_gid(self, parse_gid, setgid, setuid):
  262. parse_gid.return_value = 50001
  263. maybe_drop_privileges(gid='group')
  264. parse_gid.assert_called_with('group')
  265. setgid.assert_called_with(50001)
  266. setuid.assert_not_called()
  267. @skip.if_win32()
  268. class test_setget_uid_gid:
  269. @patch('celery.platforms.parse_uid')
  270. @patch('os.setuid')
  271. def test_setuid(self, _setuid, parse_uid):
  272. parse_uid.return_value = 5001
  273. setuid('user')
  274. parse_uid.assert_called_with('user')
  275. _setuid.assert_called_with(5001)
  276. @patch('celery.platforms.parse_gid')
  277. @patch('os.setgid')
  278. def test_setgid(self, _setgid, parse_gid):
  279. parse_gid.return_value = 50001
  280. setgid('group')
  281. parse_gid.assert_called_with('group')
  282. _setgid.assert_called_with(50001)
  283. def test_parse_uid_when_int(self):
  284. assert parse_uid(5001) == 5001
  285. @patch('pwd.getpwnam')
  286. def test_parse_uid_when_existing_name(self, getpwnam):
  287. class pwent(object):
  288. pw_uid = 5001
  289. getpwnam.return_value = pwent()
  290. assert parse_uid('user') == 5001
  291. @patch('pwd.getpwnam')
  292. def test_parse_uid_when_nonexisting_name(self, getpwnam):
  293. getpwnam.side_effect = KeyError('user')
  294. with pytest.raises(KeyError):
  295. parse_uid('user')
  296. def test_parse_gid_when_int(self):
  297. assert parse_gid(50001) == 50001
  298. @patch('grp.getgrnam')
  299. def test_parse_gid_when_existing_name(self, getgrnam):
  300. class grent(object):
  301. gr_gid = 50001
  302. getgrnam.return_value = grent()
  303. assert parse_gid('group') == 50001
  304. @patch('grp.getgrnam')
  305. def test_parse_gid_when_nonexisting_name(self, getgrnam):
  306. getgrnam.side_effect = KeyError('group')
  307. with pytest.raises(KeyError):
  308. parse_gid('group')
  309. @skip.if_win32()
  310. class test_initgroups:
  311. @patch('pwd.getpwuid')
  312. @patch('os.initgroups', create=True)
  313. def test_with_initgroups(self, initgroups_, getpwuid):
  314. getpwuid.return_value = ['user']
  315. initgroups(5001, 50001)
  316. initgroups_.assert_called_with('user', 50001)
  317. @patch('celery.platforms.setgroups')
  318. @patch('grp.getgrall')
  319. @patch('pwd.getpwuid')
  320. def test_without_initgroups(self, getpwuid, getgrall, setgroups):
  321. prev = getattr(os, 'initgroups', None)
  322. try:
  323. delattr(os, 'initgroups')
  324. except AttributeError:
  325. pass
  326. try:
  327. getpwuid.return_value = ['user']
  328. class grent(object):
  329. gr_mem = ['user']
  330. def __init__(self, gid):
  331. self.gr_gid = gid
  332. getgrall.return_value = [grent(1), grent(2), grent(3)]
  333. initgroups(5001, 50001)
  334. setgroups.assert_called_with([1, 2, 3])
  335. finally:
  336. if prev:
  337. os.initgroups = prev
  338. @skip.if_win32()
  339. class test_detached:
  340. def test_without_resource(self):
  341. prev, platforms.resource = platforms.resource, None
  342. try:
  343. with pytest.raises(RuntimeError):
  344. detached()
  345. finally:
  346. platforms.resource = prev
  347. @patch('celery.platforms._create_pidlock')
  348. @patch('celery.platforms.signals')
  349. @patch('celery.platforms.maybe_drop_privileges')
  350. @patch('os.geteuid')
  351. @patch(mock.open_fqdn)
  352. def test_default(self, open, geteuid, maybe_drop,
  353. signals, pidlock):
  354. geteuid.return_value = 0
  355. context = detached(uid='user', gid='group')
  356. assert isinstance(context, DaemonContext)
  357. signals.reset.assert_called_with('SIGCLD')
  358. maybe_drop.assert_called_with(uid='user', gid='group')
  359. open.return_value = Mock()
  360. geteuid.return_value = 5001
  361. context = detached(uid='user', gid='group', logfile='/foo/bar')
  362. assert isinstance(context, DaemonContext)
  363. assert context.after_chdir
  364. context.after_chdir()
  365. open.assert_called_with('/foo/bar', 'a')
  366. open.return_value.close.assert_called_with()
  367. context = detached(pidfile='/foo/bar/pid')
  368. assert isinstance(context, DaemonContext)
  369. assert context.after_chdir
  370. context.after_chdir()
  371. pidlock.assert_called_with('/foo/bar/pid')
  372. @skip.if_win32()
  373. class test_DaemonContext:
  374. @patch('multiprocessing.util._run_after_forkers')
  375. @patch('os.fork')
  376. @patch('os.setsid')
  377. @patch('os._exit')
  378. @patch('os.chdir')
  379. @patch('os.umask')
  380. @patch('os.close')
  381. @patch('os.closerange')
  382. @patch('os.open')
  383. @patch('os.dup2')
  384. @patch('celery.platforms.close_open_fds')
  385. def test_open(self, _close_fds, dup2, open, close, closer, umask, chdir,
  386. _exit, setsid, fork, run_after_forkers):
  387. x = DaemonContext(workdir='/opt/workdir', umask=0o22)
  388. x.stdfds = [0, 1, 2]
  389. fork.return_value = 0
  390. with x:
  391. assert x._is_open
  392. with x:
  393. pass
  394. assert fork.call_count == 2
  395. setsid.assert_called_with()
  396. _exit.assert_not_called()
  397. chdir.assert_called_with(x.workdir)
  398. umask.assert_called_with(0o22)
  399. dup2.assert_called()
  400. fork.reset_mock()
  401. fork.return_value = 1
  402. x = DaemonContext(workdir='/opt/workdir')
  403. x.stdfds = [0, 1, 2]
  404. with x:
  405. pass
  406. assert fork.call_count == 1
  407. _exit.assert_called_with(0)
  408. x = DaemonContext(workdir='/opt/workdir', fake=True)
  409. x.stdfds = [0, 1, 2]
  410. x._detach = Mock()
  411. with x:
  412. pass
  413. x._detach.assert_not_called()
  414. x.after_chdir = Mock()
  415. with x:
  416. pass
  417. x.after_chdir.assert_called_with()
  418. x = DaemonContext(workdir='/opt/workdir', umask='0755')
  419. assert x.umask == 493
  420. x = DaemonContext(workdir='/opt/workdir', umask='493')
  421. assert x.umask == 493
  422. x.redirect_to_null(None)
  423. with patch('celery.platforms.mputil') as mputil:
  424. x = DaemonContext(after_forkers=True)
  425. x.open()
  426. mputil._run_after_forkers.assert_called_with()
  427. x = DaemonContext(after_forkers=False)
  428. x.open()
  429. @skip.if_win32()
  430. class test_Pidfile:
  431. @patch('celery.platforms.Pidfile')
  432. def test_create_pidlock(self, Pidfile):
  433. p = Pidfile.return_value = Mock()
  434. p.is_locked.return_value = True
  435. p.remove_if_stale.return_value = False
  436. with mock.stdouts() as (_, err):
  437. with pytest.raises(SystemExit):
  438. create_pidlock('/var/pid')
  439. assert 'already exists' in err.getvalue()
  440. p.remove_if_stale.return_value = True
  441. ret = create_pidlock('/var/pid')
  442. assert ret is p
  443. def test_context(self):
  444. p = Pidfile('/var/pid')
  445. p.write_pid = Mock()
  446. p.remove = Mock()
  447. with p as _p:
  448. assert _p is p
  449. p.write_pid.assert_called_with()
  450. p.remove.assert_called_with()
  451. def test_acquire_raises_LockFailed(self):
  452. p = Pidfile('/var/pid')
  453. p.write_pid = Mock()
  454. p.write_pid.side_effect = OSError()
  455. with pytest.raises(LockFailed):
  456. with p:
  457. pass
  458. @patch('os.path.exists')
  459. def test_is_locked(self, exists):
  460. p = Pidfile('/var/pid')
  461. exists.return_value = True
  462. assert p.is_locked()
  463. exists.return_value = False
  464. assert not p.is_locked()
  465. def test_read_pid(self):
  466. with mock.open() as s:
  467. s.write('1816\n')
  468. s.seek(0)
  469. p = Pidfile('/var/pid')
  470. assert p.read_pid() == 1816
  471. def test_read_pid_partially_written(self):
  472. with mock.open() as s:
  473. s.write('1816')
  474. s.seek(0)
  475. p = Pidfile('/var/pid')
  476. with pytest.raises(ValueError):
  477. p.read_pid()
  478. def test_read_pid_raises_ENOENT(self):
  479. exc = IOError()
  480. exc.errno = errno.ENOENT
  481. with mock.open(side_effect=exc):
  482. p = Pidfile('/var/pid')
  483. assert p.read_pid() is None
  484. def test_read_pid_raises_IOError(self):
  485. exc = IOError()
  486. exc.errno = errno.EAGAIN
  487. with mock.open(side_effect=exc):
  488. p = Pidfile('/var/pid')
  489. with pytest.raises(IOError):
  490. p.read_pid()
  491. def test_read_pid_bogus_pidfile(self):
  492. with mock.open() as s:
  493. s.write('eighteensixteen\n')
  494. s.seek(0)
  495. p = Pidfile('/var/pid')
  496. with pytest.raises(ValueError):
  497. p.read_pid()
  498. @patch('os.unlink')
  499. def test_remove(self, unlink):
  500. unlink.return_value = True
  501. p = Pidfile('/var/pid')
  502. p.remove()
  503. unlink.assert_called_with(p.path)
  504. @patch('os.unlink')
  505. def test_remove_ENOENT(self, unlink):
  506. exc = OSError()
  507. exc.errno = errno.ENOENT
  508. unlink.side_effect = exc
  509. p = Pidfile('/var/pid')
  510. p.remove()
  511. unlink.assert_called_with(p.path)
  512. @patch('os.unlink')
  513. def test_remove_EACCES(self, unlink):
  514. exc = OSError()
  515. exc.errno = errno.EACCES
  516. unlink.side_effect = exc
  517. p = Pidfile('/var/pid')
  518. p.remove()
  519. unlink.assert_called_with(p.path)
  520. @patch('os.unlink')
  521. def test_remove_OSError(self, unlink):
  522. exc = OSError()
  523. exc.errno = errno.EAGAIN
  524. unlink.side_effect = exc
  525. p = Pidfile('/var/pid')
  526. with pytest.raises(OSError):
  527. p.remove()
  528. unlink.assert_called_with(p.path)
  529. @patch('os.kill')
  530. def test_remove_if_stale_process_alive(self, kill):
  531. p = Pidfile('/var/pid')
  532. p.read_pid = Mock()
  533. p.read_pid.return_value = 1816
  534. kill.return_value = 0
  535. assert not p.remove_if_stale()
  536. kill.assert_called_with(1816, 0)
  537. p.read_pid.assert_called_with()
  538. kill.side_effect = OSError()
  539. kill.side_effect.errno = errno.ENOENT
  540. assert not p.remove_if_stale()
  541. @patch('os.kill')
  542. def test_remove_if_stale_process_dead(self, kill):
  543. with mock.stdouts():
  544. p = Pidfile('/var/pid')
  545. p.read_pid = Mock()
  546. p.read_pid.return_value = 1816
  547. p.remove = Mock()
  548. exc = OSError()
  549. exc.errno = errno.ESRCH
  550. kill.side_effect = exc
  551. assert p.remove_if_stale()
  552. kill.assert_called_with(1816, 0)
  553. p.remove.assert_called_with()
  554. def test_remove_if_stale_broken_pid(self):
  555. with mock.stdouts():
  556. p = Pidfile('/var/pid')
  557. p.read_pid = Mock()
  558. p.read_pid.side_effect = ValueError()
  559. p.remove = Mock()
  560. assert p.remove_if_stale()
  561. p.remove.assert_called_with()
  562. def test_remove_if_stale_no_pidfile(self):
  563. p = Pidfile('/var/pid')
  564. p.read_pid = Mock()
  565. p.read_pid.return_value = None
  566. p.remove = Mock()
  567. assert p.remove_if_stale()
  568. p.remove.assert_called_with()
  569. @patch('os.fsync')
  570. @patch('os.getpid')
  571. @patch('os.open')
  572. @patch('os.fdopen')
  573. @patch(mock.open_fqdn)
  574. def test_write_pid(self, open_, fdopen, osopen, getpid, fsync):
  575. getpid.return_value = 1816
  576. osopen.return_value = 13
  577. w = fdopen.return_value = WhateverIO()
  578. w.close = Mock()
  579. r = open_.return_value = WhateverIO()
  580. r.write('1816\n')
  581. r.seek(0)
  582. p = Pidfile('/var/pid')
  583. p.write_pid()
  584. w.seek(0)
  585. assert w.readline() == '1816\n'
  586. w.close.assert_called()
  587. getpid.assert_called_with()
  588. osopen.assert_called_with(
  589. p.path, platforms.PIDFILE_FLAGS, platforms.PIDFILE_MODE,
  590. )
  591. fdopen.assert_called_with(13, 'w')
  592. fsync.assert_called_with(13)
  593. open_.assert_called_with(p.path)
  594. @patch('os.fsync')
  595. @patch('os.getpid')
  596. @patch('os.open')
  597. @patch('os.fdopen')
  598. @patch(mock.open_fqdn)
  599. def test_write_reread_fails(self, open_, fdopen,
  600. osopen, getpid, fsync):
  601. getpid.return_value = 1816
  602. osopen.return_value = 13
  603. w = fdopen.return_value = WhateverIO()
  604. w.close = Mock()
  605. r = open_.return_value = WhateverIO()
  606. r.write('11816\n')
  607. r.seek(0)
  608. p = Pidfile('/var/pid')
  609. with pytest.raises(LockFailed):
  610. p.write_pid()
  611. class test_setgroups:
  612. @patch('os.setgroups', create=True)
  613. def test_setgroups_hack_ValueError(self, setgroups):
  614. def on_setgroups(groups):
  615. if len(groups) <= 200:
  616. setgroups.return_value = True
  617. return
  618. raise ValueError()
  619. setgroups.side_effect = on_setgroups
  620. _setgroups_hack(list(range(400)))
  621. setgroups.side_effect = ValueError()
  622. with pytest.raises(ValueError):
  623. _setgroups_hack(list(range(400)))
  624. @patch('os.setgroups', create=True)
  625. def test_setgroups_hack_OSError(self, setgroups):
  626. exc = OSError()
  627. exc.errno = errno.EINVAL
  628. def on_setgroups(groups):
  629. if len(groups) <= 200:
  630. setgroups.return_value = True
  631. return
  632. raise exc
  633. setgroups.side_effect = on_setgroups
  634. _setgroups_hack(list(range(400)))
  635. setgroups.side_effect = exc
  636. with pytest.raises(OSError):
  637. _setgroups_hack(list(range(400)))
  638. exc2 = OSError()
  639. exc.errno = errno.ESRCH
  640. setgroups.side_effect = exc2
  641. with pytest.raises(OSError):
  642. _setgroups_hack(list(range(400)))
  643. @skip.if_win32()
  644. @patch('celery.platforms._setgroups_hack')
  645. def test_setgroups(self, hack):
  646. with patch('os.sysconf') as sysconf:
  647. sysconf.return_value = 100
  648. setgroups(list(range(400)))
  649. hack.assert_called_with(list(range(100)))
  650. @skip.if_win32()
  651. @patch('celery.platforms._setgroups_hack')
  652. def test_setgroups_sysconf_raises(self, hack):
  653. with patch('os.sysconf') as sysconf:
  654. sysconf.side_effect = ValueError()
  655. setgroups(list(range(400)))
  656. hack.assert_called_with(list(range(400)))
  657. @skip.if_win32()
  658. @patch('os.getgroups')
  659. @patch('celery.platforms._setgroups_hack')
  660. def test_setgroups_raises_ESRCH(self, hack, getgroups):
  661. with patch('os.sysconf') as sysconf:
  662. sysconf.side_effect = ValueError()
  663. esrch = OSError()
  664. esrch.errno = errno.ESRCH
  665. hack.side_effect = esrch
  666. with pytest.raises(OSError):
  667. setgroups(list(range(400)))
  668. @skip.if_win32()
  669. @patch('os.getgroups')
  670. @patch('celery.platforms._setgroups_hack')
  671. def test_setgroups_raises_EPERM(self, hack, getgroups):
  672. with patch('os.sysconf') as sysconf:
  673. sysconf.side_effect = ValueError()
  674. eperm = OSError()
  675. eperm.errno = errno.EPERM
  676. hack.side_effect = eperm
  677. getgroups.return_value = list(range(400))
  678. setgroups(list(range(400)))
  679. getgroups.assert_called_with()
  680. getgroups.return_value = [1000]
  681. with pytest.raises(OSError):
  682. setgroups(list(range(400)))
  683. getgroups.assert_called_with()
  684. def test_check_privileges():
  685. class Obj(object):
  686. fchown = 13
  687. prev, platforms.os = platforms.os, Obj()
  688. try:
  689. with pytest.raises(SecurityError):
  690. check_privileges({'pickle'})
  691. finally:
  692. platforms.os = prev
  693. prev, platforms.os = platforms.os, object()
  694. try:
  695. check_privileges({'pickle'})
  696. finally:
  697. platforms.os = prev