test_platforms.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833
  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.five import WhateverIO
  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(object):
  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(AssertionError):
  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(AssertionError):
  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(RuntimeError):
  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(object):
  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(object):
  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(object):
  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('os.fork')
  374. @patch('os.setsid')
  375. @patch('os._exit')
  376. @patch('os.chdir')
  377. @patch('os.umask')
  378. @patch('os.close')
  379. @patch('os.closerange')
  380. @patch('os.open')
  381. @patch('os.dup2')
  382. def test_open(self, dup2, open, close, closer, umask, chdir,
  383. _exit, setsid, fork):
  384. x = DaemonContext(workdir='/opt/workdir', umask=0o22)
  385. x.stdfds = [0, 1, 2]
  386. fork.return_value = 0
  387. with x:
  388. assert x._is_open
  389. with x:
  390. pass
  391. assert fork.call_count == 2
  392. setsid.assert_called_with()
  393. _exit.assert_not_called()
  394. chdir.assert_called_with(x.workdir)
  395. umask.assert_called_with(0o22)
  396. dup2.assert_called()
  397. fork.reset_mock()
  398. fork.return_value = 1
  399. x = DaemonContext(workdir='/opt/workdir')
  400. x.stdfds = [0, 1, 2]
  401. with x:
  402. pass
  403. assert fork.call_count == 1
  404. _exit.assert_called_with(0)
  405. x = DaemonContext(workdir='/opt/workdir', fake=True)
  406. x.stdfds = [0, 1, 2]
  407. x._detach = Mock()
  408. with x:
  409. pass
  410. x._detach.assert_not_called()
  411. x.after_chdir = Mock()
  412. with x:
  413. pass
  414. x.after_chdir.assert_called_with()
  415. x = DaemonContext(workdir='/opt/workdir', umask='0755')
  416. assert x.umask == 493
  417. x = DaemonContext(workdir='/opt/workdir', umask='493')
  418. assert x.umask == 493
  419. x.redirect_to_null(None)
  420. with patch('celery.platforms.mputil') as mputil:
  421. x = DaemonContext(after_forkers=True)
  422. x.open()
  423. mputil._run_after_forkers.assert_called_with()
  424. x = DaemonContext(after_forkers=False)
  425. x.open()
  426. @skip.if_win32()
  427. class test_Pidfile:
  428. @patch('celery.platforms.Pidfile')
  429. def test_create_pidlock(self, Pidfile):
  430. p = Pidfile.return_value = Mock()
  431. p.is_locked.return_value = True
  432. p.remove_if_stale.return_value = False
  433. with mock.stdouts() as (_, err):
  434. with pytest.raises(SystemExit):
  435. create_pidlock('/var/pid')
  436. assert 'already exists' in err.getvalue()
  437. p.remove_if_stale.return_value = True
  438. ret = create_pidlock('/var/pid')
  439. assert ret is p
  440. def test_context(self):
  441. p = Pidfile('/var/pid')
  442. p.write_pid = Mock()
  443. p.remove = Mock()
  444. with p as _p:
  445. assert _p is p
  446. p.write_pid.assert_called_with()
  447. p.remove.assert_called_with()
  448. def test_acquire_raises_LockFailed(self):
  449. p = Pidfile('/var/pid')
  450. p.write_pid = Mock()
  451. p.write_pid.side_effect = OSError()
  452. with pytest.raises(LockFailed):
  453. with p:
  454. pass
  455. @patch('os.path.exists')
  456. def test_is_locked(self, exists):
  457. p = Pidfile('/var/pid')
  458. exists.return_value = True
  459. assert p.is_locked()
  460. exists.return_value = False
  461. assert not p.is_locked()
  462. def test_read_pid(self):
  463. with mock.open() as s:
  464. s.write('1816\n')
  465. s.seek(0)
  466. p = Pidfile('/var/pid')
  467. assert p.read_pid() == 1816
  468. def test_read_pid_partially_written(self):
  469. with mock.open() as s:
  470. s.write('1816')
  471. s.seek(0)
  472. p = Pidfile('/var/pid')
  473. with pytest.raises(ValueError):
  474. p.read_pid()
  475. def test_read_pid_raises_ENOENT(self):
  476. exc = IOError()
  477. exc.errno = errno.ENOENT
  478. with mock.open(side_effect=exc):
  479. p = Pidfile('/var/pid')
  480. assert p.read_pid() is None
  481. def test_read_pid_raises_IOError(self):
  482. exc = IOError()
  483. exc.errno = errno.EAGAIN
  484. with mock.open(side_effect=exc):
  485. p = Pidfile('/var/pid')
  486. with pytest.raises(IOError):
  487. p.read_pid()
  488. def test_read_pid_bogus_pidfile(self):
  489. with mock.open() as s:
  490. s.write('eighteensixteen\n')
  491. s.seek(0)
  492. p = Pidfile('/var/pid')
  493. with pytest.raises(ValueError):
  494. p.read_pid()
  495. @patch('os.unlink')
  496. def test_remove(self, unlink):
  497. unlink.return_value = True
  498. p = Pidfile('/var/pid')
  499. p.remove()
  500. unlink.assert_called_with(p.path)
  501. @patch('os.unlink')
  502. def test_remove_ENOENT(self, unlink):
  503. exc = OSError()
  504. exc.errno = errno.ENOENT
  505. unlink.side_effect = exc
  506. p = Pidfile('/var/pid')
  507. p.remove()
  508. unlink.assert_called_with(p.path)
  509. @patch('os.unlink')
  510. def test_remove_EACCES(self, unlink):
  511. exc = OSError()
  512. exc.errno = errno.EACCES
  513. unlink.side_effect = exc
  514. p = Pidfile('/var/pid')
  515. p.remove()
  516. unlink.assert_called_with(p.path)
  517. @patch('os.unlink')
  518. def test_remove_OSError(self, unlink):
  519. exc = OSError()
  520. exc.errno = errno.EAGAIN
  521. unlink.side_effect = exc
  522. p = Pidfile('/var/pid')
  523. with pytest.raises(OSError):
  524. p.remove()
  525. unlink.assert_called_with(p.path)
  526. @patch('os.kill')
  527. def test_remove_if_stale_process_alive(self, kill):
  528. p = Pidfile('/var/pid')
  529. p.read_pid = Mock()
  530. p.read_pid.return_value = 1816
  531. kill.return_value = 0
  532. assert not p.remove_if_stale()
  533. kill.assert_called_with(1816, 0)
  534. p.read_pid.assert_called_with()
  535. kill.side_effect = OSError()
  536. kill.side_effect.errno = errno.ENOENT
  537. assert not p.remove_if_stale()
  538. @patch('os.kill')
  539. def test_remove_if_stale_process_dead(self, kill):
  540. with mock.stdouts():
  541. p = Pidfile('/var/pid')
  542. p.read_pid = Mock()
  543. p.read_pid.return_value = 1816
  544. p.remove = Mock()
  545. exc = OSError()
  546. exc.errno = errno.ESRCH
  547. kill.side_effect = exc
  548. assert p.remove_if_stale()
  549. kill.assert_called_with(1816, 0)
  550. p.remove.assert_called_with()
  551. def test_remove_if_stale_broken_pid(self):
  552. with mock.stdouts():
  553. p = Pidfile('/var/pid')
  554. p.read_pid = Mock()
  555. p.read_pid.side_effect = ValueError()
  556. p.remove = Mock()
  557. assert p.remove_if_stale()
  558. p.remove.assert_called_with()
  559. def test_remove_if_stale_no_pidfile(self):
  560. p = Pidfile('/var/pid')
  561. p.read_pid = Mock()
  562. p.read_pid.return_value = None
  563. p.remove = Mock()
  564. assert p.remove_if_stale()
  565. p.remove.assert_called_with()
  566. @patch('os.fsync')
  567. @patch('os.getpid')
  568. @patch('os.open')
  569. @patch('os.fdopen')
  570. @patch(mock.open_fqdn)
  571. def test_write_pid(self, open_, fdopen, osopen, getpid, fsync):
  572. getpid.return_value = 1816
  573. osopen.return_value = 13
  574. w = fdopen.return_value = WhateverIO()
  575. w.close = Mock()
  576. r = open_.return_value = WhateverIO()
  577. r.write('1816\n')
  578. r.seek(0)
  579. p = Pidfile('/var/pid')
  580. p.write_pid()
  581. w.seek(0)
  582. assert w.readline() == '1816\n'
  583. w.close.assert_called()
  584. getpid.assert_called_with()
  585. osopen.assert_called_with(
  586. p.path, platforms.PIDFILE_FLAGS, platforms.PIDFILE_MODE,
  587. )
  588. fdopen.assert_called_with(13, 'w')
  589. fsync.assert_called_with(13)
  590. open_.assert_called_with(p.path)
  591. @patch('os.fsync')
  592. @patch('os.getpid')
  593. @patch('os.open')
  594. @patch('os.fdopen')
  595. @patch(mock.open_fqdn)
  596. def test_write_reread_fails(self, open_, fdopen,
  597. osopen, getpid, fsync):
  598. getpid.return_value = 1816
  599. osopen.return_value = 13
  600. w = fdopen.return_value = WhateverIO()
  601. w.close = Mock()
  602. r = open_.return_value = WhateverIO()
  603. r.write('11816\n')
  604. r.seek(0)
  605. p = Pidfile('/var/pid')
  606. with pytest.raises(LockFailed):
  607. p.write_pid()
  608. class test_setgroups:
  609. @patch('os.setgroups', create=True)
  610. def test_setgroups_hack_ValueError(self, setgroups):
  611. def on_setgroups(groups):
  612. if len(groups) <= 200:
  613. setgroups.return_value = True
  614. return
  615. raise ValueError()
  616. setgroups.side_effect = on_setgroups
  617. _setgroups_hack(list(range(400)))
  618. setgroups.side_effect = ValueError()
  619. with pytest.raises(ValueError):
  620. _setgroups_hack(list(range(400)))
  621. @patch('os.setgroups', create=True)
  622. def test_setgroups_hack_OSError(self, setgroups):
  623. exc = OSError()
  624. exc.errno = errno.EINVAL
  625. def on_setgroups(groups):
  626. if len(groups) <= 200:
  627. setgroups.return_value = True
  628. return
  629. raise exc
  630. setgroups.side_effect = on_setgroups
  631. _setgroups_hack(list(range(400)))
  632. setgroups.side_effect = exc
  633. with pytest.raises(OSError):
  634. _setgroups_hack(list(range(400)))
  635. exc2 = OSError()
  636. exc.errno = errno.ESRCH
  637. setgroups.side_effect = exc2
  638. with pytest.raises(OSError):
  639. _setgroups_hack(list(range(400)))
  640. @skip.if_win32()
  641. @patch('celery.platforms._setgroups_hack')
  642. def test_setgroups(self, hack):
  643. with patch('os.sysconf') as sysconf:
  644. sysconf.return_value = 100
  645. setgroups(list(range(400)))
  646. hack.assert_called_with(list(range(100)))
  647. @skip.if_win32()
  648. @patch('celery.platforms._setgroups_hack')
  649. def test_setgroups_sysconf_raises(self, hack):
  650. with patch('os.sysconf') as sysconf:
  651. sysconf.side_effect = ValueError()
  652. setgroups(list(range(400)))
  653. hack.assert_called_with(list(range(400)))
  654. @skip.if_win32()
  655. @patch('os.getgroups')
  656. @patch('celery.platforms._setgroups_hack')
  657. def test_setgroups_raises_ESRCH(self, hack, getgroups):
  658. with patch('os.sysconf') as sysconf:
  659. sysconf.side_effect = ValueError()
  660. esrch = OSError()
  661. esrch.errno = errno.ESRCH
  662. hack.side_effect = esrch
  663. with pytest.raises(OSError):
  664. setgroups(list(range(400)))
  665. @skip.if_win32()
  666. @patch('os.getgroups')
  667. @patch('celery.platforms._setgroups_hack')
  668. def test_setgroups_raises_EPERM(self, hack, getgroups):
  669. with patch('os.sysconf') as sysconf:
  670. sysconf.side_effect = ValueError()
  671. eperm = OSError()
  672. eperm.errno = errno.EPERM
  673. hack.side_effect = eperm
  674. getgroups.return_value = list(range(400))
  675. setgroups(list(range(400)))
  676. getgroups.assert_called_with()
  677. getgroups.return_value = [1000]
  678. with pytest.raises(OSError):
  679. setgroups(list(range(400)))
  680. getgroups.assert_called_with()
  681. def test_check_privileges():
  682. class Obj(object):
  683. fchown = 13
  684. prev, platforms.os = platforms.os, Obj()
  685. try:
  686. with pytest.raises(AssertionError):
  687. check_privileges({'pickle'})
  688. finally:
  689. platforms.os = prev
  690. prev, platforms.os = platforms.os, object()
  691. try:
  692. check_privileges({'pickle'})
  693. finally:
  694. platforms.os = prev