test_pip_state.py 24 KB

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