test_platforms.py 25 KB

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