1
0

test_pip_state.py 28 KB

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