test_pip_state.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Pedro Algarvio (pedro@algarvio.me)
  4. tests.integration.states.pip_state
  5. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  6. '''
  7. # Import python libs
  8. from __future__ import absolute_import, print_function, unicode_literals
  9. import errno
  10. import os
  11. import glob
  12. import pprint
  13. import shutil
  14. import sys
  15. try:
  16. import pwd
  17. HAS_PWD = True
  18. except ImportError:
  19. HAS_PWD = False
  20. # Import Salt Testing libs
  21. from tests.support.case import ModuleCase
  22. from tests.support.helpers import (
  23. destructiveTest,
  24. requires_system_grains,
  25. with_system_user,
  26. skip_if_not_root,
  27. with_tempdir
  28. )
  29. from tests.support.mixins import SaltReturnAssertsMixin
  30. from tests.support.runtests import RUNTIME_VARS
  31. from tests.support.unit import skipIf
  32. # Import salt libs
  33. import salt.utils.files
  34. import salt.utils.path
  35. import salt.utils.platform
  36. import salt.utils.versions
  37. import salt.utils.win_dacl
  38. import salt.utils.win_functions
  39. import salt.utils.win_runas
  40. from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES
  41. from salt.exceptions import CommandExecutionError
  42. # Import 3rd-party libs
  43. from salt.ext import six
  44. class VirtualEnv(object):
  45. def __init__(self, test, venv_dir):
  46. self.venv_dir = venv_dir
  47. self.test = test
  48. self.test.addCleanup(shutil.rmtree, self.venv_dir, ignore_errors=True)
  49. def __enter__(self):
  50. ret = self.test._create_virtualenv(self.venv_dir)
  51. self.test.assertEqual(
  52. ret['retcode'], 0,
  53. msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format(
  54. pprint.pformat(ret)
  55. )
  56. )
  57. def __exit__(self, *args):
  58. pass
  59. @skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed')
  60. class PipStateTest(ModuleCase, SaltReturnAssertsMixin):
  61. def _create_virtualenv(self, path):
  62. '''
  63. The reason why the virtualenv creation is proxied by this function is mostly
  64. because under windows, we can't seem to properly create a virtualenv off of
  65. another virtualenv(we can on linux) and also because, we really don't want to
  66. test virtualenv creation off of another virtualenv, we want a virtualenv created
  67. from the original python.
  68. Also, one windows, we must also point to the virtualenv binary outside the existing
  69. virtualenv because it will fail otherwise
  70. '''
  71. self.addCleanup(shutil.rmtree, path, ignore_errors=True)
  72. try:
  73. if salt.utils.is_windows():
  74. python = os.path.join(sys.real_prefix, os.path.basename(sys.executable))
  75. else:
  76. python = os.path.join(sys.real_prefix, 'bin', os.path.basename(sys.executable))
  77. # We're running off a virtualenv, and we don't want to create a virtualenv off of
  78. # a virtualenv, let's point to the actual python that created the virtualenv
  79. kwargs = {'python': python}
  80. except AttributeError:
  81. # We're running off of the system python
  82. kwargs = {}
  83. return self.run_function('virtualenv.create', [path], **kwargs)
  84. def test_pip_installed_removed(self):
  85. '''
  86. Tests installed and removed states
  87. '''
  88. name = 'pudb'
  89. if name in self.run_function('pip.list'):
  90. self.skipTest('{0} is already installed, uninstall to run this test'.format(name))
  91. ret = self.run_state('pip.installed', name=name)
  92. self.assertSaltTrueReturn(ret)
  93. ret = self.run_state('pip.removed', name=name)
  94. self.assertSaltTrueReturn(ret)
  95. def test_pip_installed_removed_venv(self):
  96. venv_dir = os.path.join(
  97. RUNTIME_VARS.TMP, 'pip_installed_removed'
  98. )
  99. with VirtualEnv(self, venv_dir):
  100. name = 'pudb'
  101. ret = self.run_state('pip.installed', name=name, bin_env=venv_dir)
  102. self.assertSaltTrueReturn(ret)
  103. ret = self.run_state('pip.removed', name=name, bin_env=venv_dir)
  104. self.assertSaltTrueReturn(ret)
  105. def test_pip_installed_errors(self):
  106. venv_dir = os.path.join(
  107. RUNTIME_VARS.TMP, 'pip-installed-errors'
  108. )
  109. def cleanup_environ(environ):
  110. os.environ.clear()
  111. os.environ.update(environ)
  112. self.addCleanup(cleanup_environ, os.environ.copy())
  113. # Since we don't have the virtualenv created, pip.installed will
  114. # throw an error.
  115. # Example error strings:
  116. # * "Error installing 'pep8': /tmp/pip-installed-errors: not found"
  117. # * "Error installing 'pep8': /bin/sh: 1: /tmp/pip-installed-errors: not found"
  118. # * "Error installing 'pep8': /bin/bash: /tmp/pip-installed-errors: No such file or directory"
  119. os.environ['SHELL'] = '/bin/sh'
  120. ret = self.run_function('state.sls', mods='pip-installed-errors')
  121. self.assertSaltFalseReturn(ret)
  122. self.assertSaltCommentRegexpMatches(
  123. ret,
  124. 'Error installing \'pep8\':'
  125. )
  126. # We now create the missing virtualenv
  127. ret = self._create_virtualenv(venv_dir)
  128. self.assertEqual(ret['retcode'], 0)
  129. # The state should not have any issues running now
  130. ret = self.run_function('state.sls', mods='pip-installed-errors')
  131. self.assertSaltTrueReturn(ret)
  132. @skipIf(six.PY3, 'Issue is specific to carbon module, which is PY2-only')
  133. @skipIf(salt.utils.platform.is_windows(), "Carbon does not install in Windows")
  134. @requires_system_grains
  135. def test_pip_installed_weird_install(self, grains=None):
  136. # First, check to see if this is running on CentOS 5 or MacOS.
  137. # If so, skip this test.
  138. if grains['os'] in ('CentOS',) and grains['osrelease_info'][0] in (5,):
  139. self.skipTest('This test does not run reliably on CentOS 5')
  140. if grains['os'] in ('MacOS',):
  141. self.skipTest('This test does not run reliably on MacOS')
  142. ographite = '/opt/graphite'
  143. if os.path.isdir(ographite):
  144. self.skipTest(
  145. 'You already have \'{0}\'. This test would overwrite this '
  146. 'directory'.format(ographite)
  147. )
  148. try:
  149. os.makedirs(ographite)
  150. except OSError as err:
  151. if err.errno == errno.EACCES:
  152. # Permission denied
  153. self.skipTest(
  154. 'You don\'t have the required permissions to run this test'
  155. )
  156. finally:
  157. if os.path.isdir(ographite):
  158. shutil.rmtree(ographite, ignore_errors=True)
  159. venv_dir = os.path.join(RUNTIME_VARS.TMP, 'pip-installed-weird-install')
  160. try:
  161. # We may be able to remove this, I had to add it because the custom
  162. # modules from the test suite weren't available in the jinja
  163. # context when running the call to state.sls that comes after.
  164. self.run_function('saltutil.sync_modules')
  165. # Since we don't have the virtualenv created, pip.installed will
  166. # throw an error.
  167. ret = self.run_function(
  168. 'state.sls', mods='pip-installed-weird-install'
  169. )
  170. self.assertSaltTrueReturn(ret)
  171. # We cannot use assertInSaltComment here because we need to skip
  172. # some of the state return parts
  173. for key in six.iterkeys(ret):
  174. self.assertTrue(ret[key]['result'])
  175. if ret[key]['name'] != 'carbon < 1.1':
  176. continue
  177. self.assertEqual(
  178. ret[key]['comment'],
  179. 'There was no error installing package \'carbon < 1.1\' '
  180. 'although it does not show when calling \'pip.freeze\'.'
  181. )
  182. break
  183. else:
  184. raise Exception('Expected state did not run')
  185. finally:
  186. if os.path.isdir(ographite):
  187. shutil.rmtree(ographite, ignore_errors=True)
  188. def test_issue_2028_pip_installed_state(self):
  189. ret = self.run_function('state.sls', mods='issue-2028-pip-installed')
  190. venv_dir = os.path.join(
  191. RUNTIME_VARS.TMP, 'issue-2028-pip-installed'
  192. )
  193. self.addCleanup(shutil.rmtree, venv_dir, ignore_errors=True)
  194. pep8_bin = os.path.join(venv_dir, 'bin', 'pep8')
  195. if salt.utils.platform.is_windows():
  196. pep8_bin = os.path.join(venv_dir, 'Scripts', 'pep8.exe')
  197. self.assertSaltTrueReturn(ret)
  198. self.assertTrue(
  199. os.path.isfile(pep8_bin)
  200. )
  201. def test_issue_2087_missing_pip(self):
  202. venv_dir = os.path.join(
  203. RUNTIME_VARS.TMP, 'issue-2087-missing-pip'
  204. )
  205. # Let's create the testing virtualenv
  206. ret = self._create_virtualenv(venv_dir)
  207. self.assertEqual(
  208. ret['retcode'], 0,
  209. msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format(
  210. pprint.pformat(ret)
  211. )
  212. )
  213. # Let's remove the pip binary
  214. pip_bin = os.path.join(venv_dir, 'bin', 'pip')
  215. site_dir = self.run_function('virtualenv.get_distribution_path', [venv_dir, 'pip'])
  216. if salt.utils.platform.is_windows():
  217. pip_bin = os.path.join(venv_dir, 'Scripts', 'pip.exe')
  218. site_dir = os.path.join(venv_dir, 'lib', 'site-packages')
  219. if not os.path.isfile(pip_bin):
  220. self.skipTest(
  221. 'Failed to find the pip binary to the test virtualenv'
  222. )
  223. os.remove(pip_bin)
  224. # Also remove the pip dir from site-packages
  225. # This is needed now that we're using python -m pip instead of the
  226. # pip binary directly. python -m pip will still work even if the
  227. # pip binary is missing
  228. shutil.rmtree(os.path.join(site_dir, 'pip'))
  229. # Let's run the state which should fail because pip is missing
  230. ret = self.run_function('state.sls', mods='issue-2087-missing-pip')
  231. self.assertSaltFalseReturn(ret)
  232. self.assertInSaltComment(
  233. 'Error installing \'pep8\': Could not find a `pip` binary',
  234. ret
  235. )
  236. def test_issue_5940_multiple_pip_mirrors(self):
  237. '''
  238. Test multiple pip mirrors. This test only works with pip < 7.0.0
  239. '''
  240. ret = self.run_function(
  241. 'state.sls', mods='issue-5940-multiple-pip-mirrors'
  242. )
  243. venv_dir = os.path.join(
  244. RUNTIME_VARS.TMP, '5940-multiple-pip-mirrors'
  245. )
  246. self.addCleanup(shutil.rmtree, venv_dir, ignore_errors=True)
  247. try:
  248. self.assertSaltTrueReturn(ret)
  249. self.assertTrue(
  250. os.path.isfile(os.path.join(venv_dir, 'bin', 'pep8'))
  251. )
  252. except (AssertionError, CommandExecutionError):
  253. pip_version = self.run_function('pip.version', [venv_dir])
  254. if salt.utils.versions.compare(ver1=pip_version, oper='>=', ver2='7.0.0'):
  255. self.skipTest('the --mirrors arg has been deprecated and removed in pip==7.0.0')
  256. @destructiveTest
  257. @skip_if_not_root
  258. @with_system_user('issue-6912', on_existing='delete', delete=True,
  259. password='PassWord1!')
  260. @with_tempdir()
  261. def test_issue_6912_wrong_owner(self, temp_dir, username):
  262. # Setup virtual environment directory to be used throughout the test
  263. venv_dir = os.path.join(temp_dir, '6912-wrong-owner')
  264. # The virtual environment needs to be in a location that is accessible
  265. # by both the user running the test and the runas user
  266. if salt.utils.platform.is_windows():
  267. salt.utils.win_dacl.set_permissions(temp_dir, username, 'full_control')
  268. else:
  269. uid = self.run_function('file.user_to_uid', [username])
  270. os.chown(temp_dir, uid, -1)
  271. # Create the virtual environment
  272. venv_create = self.run_function(
  273. 'virtualenv.create', [venv_dir], user=username,
  274. password='PassWord1!')
  275. if venv_create['retcode'] > 0:
  276. self.skipTest('Failed to create testcase virtual environment: {0}'
  277. ''.format(venv_create))
  278. # pip install passing the package name in `name`
  279. ret = self.run_state(
  280. 'pip.installed', name='pep8', user=username, bin_env=venv_dir,
  281. password='PassWord1!')
  282. self.assertSaltTrueReturn(ret)
  283. if HAS_PWD:
  284. uid = pwd.getpwnam(username).pw_uid
  285. for globmatch in (os.path.join(venv_dir, '**', 'pep8*'),
  286. os.path.join(venv_dir, '*', '**', 'pep8*'),
  287. os.path.join(venv_dir, '*', '*', '**', 'pep8*')):
  288. for path in glob.glob(globmatch):
  289. if HAS_PWD:
  290. self.assertEqual(uid, os.stat(path).st_uid)
  291. elif salt.utils.platform.is_windows():
  292. self.assertEqual(
  293. salt.utils.win_dacl.get_owner(path), username)
  294. @destructiveTest
  295. @skip_if_not_root
  296. @skipIf(salt.utils.platform.is_darwin(), 'Test is flaky on macosx')
  297. @with_system_user('issue-6912', on_existing='delete', delete=True,
  298. password='PassWord1!')
  299. @with_tempdir()
  300. def test_issue_6912_wrong_owner_requirements_file(self, temp_dir, username):
  301. # Setup virtual environment directory to be used throughout the test
  302. venv_dir = os.path.join(temp_dir, '6912-wrong-owner')
  303. # The virtual environment needs to be in a location that is accessible
  304. # by both the user running the test and the runas user
  305. if salt.utils.platform.is_windows():
  306. salt.utils.win_dacl.set_permissions(temp_dir, username, 'full_control')
  307. else:
  308. uid = self.run_function('file.user_to_uid', [username])
  309. os.chown(temp_dir, uid, -1)
  310. # Create the virtual environment again as it should have been removed
  311. venv_create = self.run_function(
  312. 'virtualenv.create', [venv_dir], user=username,
  313. password='PassWord1!')
  314. if venv_create['retcode'] > 0:
  315. self.skipTest('failed to create testcase virtual environment: {0}'
  316. ''.format(venv_create))
  317. # pip install using a requirements file
  318. req_filename = os.path.join(
  319. RUNTIME_VARS.TMP_STATE_TREE, 'issue-6912-requirements.txt'
  320. )
  321. with salt.utils.files.fopen(req_filename, 'wb') as reqf:
  322. reqf.write(b'pep8\n')
  323. ret = self.run_state(
  324. 'pip.installed', name='', user=username, bin_env=venv_dir,
  325. requirements='salt://issue-6912-requirements.txt',
  326. password='PassWord1!')
  327. self.assertSaltTrueReturn(ret)
  328. if HAS_PWD:
  329. uid = pwd.getpwnam(username).pw_uid
  330. for globmatch in (os.path.join(venv_dir, '**', 'pep8*'),
  331. os.path.join(venv_dir, '*', '**', 'pep8*'),
  332. os.path.join(venv_dir, '*', '*', '**', 'pep8*')):
  333. for path in glob.glob(globmatch):
  334. if HAS_PWD:
  335. self.assertEqual(uid, os.stat(path).st_uid)
  336. elif salt.utils.platform.is_windows():
  337. self.assertEqual(
  338. salt.utils.win_dacl.get_owner(path), username)
  339. def test_issue_6833_pip_upgrade_pip(self):
  340. # Create the testing virtualenv
  341. venv_dir = os.path.join(
  342. RUNTIME_VARS.TMP, '6833-pip-upgrade-pip'
  343. )
  344. ret = self._create_virtualenv(venv_dir)
  345. self.assertEqual(
  346. ret['retcode'], 0,
  347. msg='Expected \'retcode\' key did not match. Full return dictionary:\n{}'.format(
  348. pprint.pformat(ret)
  349. )
  350. )
  351. self.assertIn(
  352. 'New python executable',
  353. ret['stdout'],
  354. msg='Expected STDOUT did not match. Full return dictionary:\n{}'.format(
  355. pprint.pformat(ret)
  356. )
  357. )
  358. # Let's install a fixed version pip over whatever pip was
  359. # previously installed
  360. ret = self.run_function(
  361. 'pip.install', ['pip==8.0'], upgrade=True,
  362. bin_env=venv_dir
  363. )
  364. if not isinstance(ret, dict):
  365. self.fail(
  366. 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret)
  367. )
  368. self.assertEqual(ret['retcode'], 0)
  369. self.assertIn(
  370. 'Successfully installed pip',
  371. ret['stdout']
  372. )
  373. # Let's make sure we have pip 8.0 installed
  374. self.assertEqual(
  375. self.run_function('pip.list', ['pip'], bin_env=venv_dir),
  376. {'pip': '8.0.0'}
  377. )
  378. # Now the actual pip upgrade pip test
  379. ret = self.run_state(
  380. 'pip.installed', name='pip==8.0.1', upgrade=True,
  381. bin_env=venv_dir
  382. )
  383. if not isinstance(ret, dict):
  384. self.fail(
  385. 'The \'pip.install\' command did not return the excepted dictionary. Output:\n{}'.format(ret)
  386. )
  387. self.assertSaltTrueReturn(ret)
  388. self.assertSaltStateChangesEqual(ret, {'pip==8.0.1': 'Installed'})
  389. def test_pip_installed_specific_env(self):
  390. # Create the testing virtualenv
  391. venv_dir = os.path.join(
  392. RUNTIME_VARS.TMP, 'pip-installed-specific-env'
  393. )
  394. # Let's write a requirements file
  395. requirements_file = os.path.join(
  396. RUNTIME_VARS.TMP_PRODENV_STATE_TREE, 'prod-env-requirements.txt'
  397. )
  398. with salt.utils.files.fopen(requirements_file, 'wb') as reqf:
  399. reqf.write(b'pep8\n')
  400. try:
  401. self._create_virtualenv(venv_dir)
  402. # The requirements file should not be found the base environment
  403. ret = self.run_state(
  404. 'pip.installed', name='', bin_env=venv_dir,
  405. requirements='salt://prod-env-requirements.txt'
  406. )
  407. self.assertSaltFalseReturn(ret)
  408. self.assertInSaltComment(
  409. "'salt://prod-env-requirements.txt' not found", ret
  410. )
  411. # The requirements file must be found in the prod environment
  412. ret = self.run_state(
  413. 'pip.installed', name='', bin_env=venv_dir, saltenv='prod',
  414. requirements='salt://prod-env-requirements.txt'
  415. )
  416. self.assertSaltTrueReturn(ret)
  417. self.assertInSaltComment(
  418. 'Successfully processed requirements file '
  419. 'salt://prod-env-requirements.txt', ret
  420. )
  421. # We're using the base environment but we're passing the prod
  422. # environment as an url arg to salt://
  423. ret = self.run_state(
  424. 'pip.installed', name='', bin_env=venv_dir,
  425. requirements='salt://prod-env-requirements.txt?saltenv=prod'
  426. )
  427. self.assertSaltTrueReturn(ret)
  428. self.assertInSaltComment(
  429. 'Requirements were already installed.',
  430. ret
  431. )
  432. finally:
  433. if os.path.isfile(requirements_file):
  434. os.unlink(requirements_file)
  435. @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2')
  436. def test_22359_pip_installed_unless_does_not_trigger_warnings(self):
  437. # This test case should be moved to a format_call unit test specific to
  438. # the state internal keywords
  439. venv_dir = os.path.join(RUNTIME_VARS.TMP, 'pip-installed-unless')
  440. venv_create = self._create_virtualenv(venv_dir)
  441. if venv_create['retcode'] > 0:
  442. self.skipTest(
  443. 'Failed to create testcase virtual environment: {0}'.format(
  444. venv_create
  445. )
  446. )
  447. false_cmd = salt.utils.path.which('false')
  448. if salt.utils.platform.is_windows():
  449. false_cmd = 'exit 1 >nul'
  450. try:
  451. ret = self.run_state(
  452. 'pip.installed', name='pep8', bin_env=venv_dir, unless=false_cmd, timeout=600
  453. )
  454. self.assertSaltTrueReturn(ret)
  455. self.assertNotIn('warnings', next(six.itervalues(ret)))
  456. finally:
  457. if os.path.isdir(venv_dir):
  458. shutil.rmtree(venv_dir, ignore_errors=True)
  459. @skipIf(sys.version_info[:2] >= (3, 6), 'Old version of virtualenv too old for python3.6')
  460. @skipIf(salt.utils.platform.is_windows(), "Carbon does not install in Windows")
  461. def test_46127_pip_env_vars(self):
  462. '''
  463. Test that checks if env_vars passed to pip.installed are also passed
  464. to pip.freeze while checking for existing installations
  465. '''
  466. # This issue is most easily checked while installing carbon
  467. # Much of the code here comes from the test_weird_install function above
  468. ographite = '/opt/graphite'
  469. if os.path.isdir(ographite):
  470. self.skipTest(
  471. 'You already have \'{0}\'. This test would overwrite this '
  472. 'directory'.format(ographite)
  473. )
  474. try:
  475. os.makedirs(ographite)
  476. except OSError as err:
  477. if err.errno == errno.EACCES:
  478. # Permission denied
  479. self.skipTest(
  480. 'You don\'t have the required permissions to run this test'
  481. )
  482. finally:
  483. if os.path.isdir(ographite):
  484. shutil.rmtree(ographite, ignore_errors=True)
  485. venv_dir = os.path.join(RUNTIME_VARS.TMP, 'issue-46127-pip-env-vars')
  486. try:
  487. # We may be able to remove this, I had to add it because the custom
  488. # modules from the test suite weren't available in the jinja
  489. # context when running the call to state.sls that comes after.
  490. self.run_function('saltutil.sync_modules')
  491. # Since we don't have the virtualenv created, pip.installed will
  492. # throw an error.
  493. ret = self.run_function(
  494. 'state.sls', mods='issue-46127-pip-env-vars'
  495. )
  496. self.assertSaltTrueReturn(ret)
  497. for key in six.iterkeys(ret):
  498. self.assertTrue(ret[key]['result'])
  499. if ret[key]['name'] != 'carbon < 1.3':
  500. continue
  501. self.assertEqual(
  502. ret[key]['comment'],
  503. 'All packages were successfully installed'
  504. )
  505. break
  506. else:
  507. raise Exception('Expected state did not run')
  508. # Run the state again. Now the already installed message should
  509. # appear
  510. ret = self.run_function(
  511. 'state.sls', mods='issue-46127-pip-env-vars'
  512. )
  513. self.assertSaltTrueReturn(ret)
  514. # We cannot use assertInSaltComment here because we need to skip
  515. # some of the state return parts
  516. for key in six.iterkeys(ret):
  517. self.assertTrue(ret[key]['result'])
  518. # As we are re-running the formula, some states will not be run
  519. # and "name" may or may not be present, so we use .get() pattern
  520. if ret[key].get('name', '') != 'carbon < 1.3':
  521. continue
  522. self.assertEqual(
  523. ret[key]['comment'],
  524. ('All packages were successfully installed'))
  525. break
  526. else:
  527. raise Exception('Expected state did not run')
  528. finally:
  529. if os.path.isdir(ographite):
  530. shutil.rmtree(ographite, ignore_errors=True)
  531. if os.path.isdir(venv_dir):
  532. shutil.rmtree(venv_dir)
  533. class PipStateInRequisiteTest(ModuleCase, SaltReturnAssertsMixin):
  534. @with_tempdir()
  535. def test_issue_54755(self, tmpdir):
  536. '''
  537. Verify github issue 54755 is resolved. This only fails when there is no
  538. pip module in the python environment. Since the test suite normally has
  539. a pip module this test will pass and is here for posterity. See also
  540. unit.states.test_pip_state.PipStateUtilsTest.test_pip_purge_method_with_pip
  541. and
  542. unit.states.test_pip_state.PipStateUtilsTest.test_pip_purge_method_without_pip
  543. Which also validate this issue and will pass/fail regardless of whether
  544. or not pip is installed.
  545. '''
  546. file_path = os.path.join(tmpdir, 'issue-54755')
  547. ret = self.run_function('state.sls', mods='issue-54755', pillar={'file_path': file_path})
  548. key = 'file_|-issue-54755_|-{}_|-managed'.format(file_path)
  549. assert key in ret
  550. assert ret[key]['result'] is True
  551. with salt.utils.files.fopen(file_path, 'r') as fp:
  552. assert fp.read().strip() == 'issue-54755'