test_platforms.py 25 KB

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