test_pip.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Pedro Algarvio (pedro@algarvio.me)
  4. tests.integration.modules.pip
  5. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  6. '''
  7. # Import python libs
  8. from __future__ import absolute_import, print_function, unicode_literals
  9. import os
  10. import re
  11. import shutil
  12. import tempfile
  13. # Import Salt Testing libs
  14. from tests.support.case import ModuleCase
  15. from tests.support.unit import skipIf
  16. from tests.support.paths import TMP
  17. from tests.support.helpers import skip_if_not_root
  18. # Import salt libs
  19. import salt.utils.files
  20. import salt.utils.path
  21. import salt.utils.platform
  22. from salt.modules.virtualenv_mod import KNOWN_BINARY_NAMES
  23. @skipIf(salt.utils.path.which_bin(KNOWN_BINARY_NAMES) is None, 'virtualenv not installed')
  24. class PipModuleTest(ModuleCase):
  25. def setUp(self):
  26. super(PipModuleTest, self).setUp()
  27. self.venv_test_dir = tempfile.mkdtemp(dir=TMP)
  28. self.venv_dir = os.path.join(self.venv_test_dir, 'venv')
  29. for key in os.environ.copy():
  30. if key.startswith('PIP_'):
  31. os.environ.pop(key)
  32. self.pip_temp = os.path.join(self.venv_test_dir, '.pip-temp')
  33. if not os.path.isdir(self.pip_temp):
  34. os.makedirs(self.pip_temp)
  35. os.environ['PIP_SOURCE_DIR'] = os.environ['PIP_BUILD_DIR'] = ''
  36. def tearDown(self):
  37. super(PipModuleTest, self).tearDown()
  38. if os.path.isdir(self.venv_test_dir):
  39. shutil.rmtree(self.venv_test_dir, ignore_errors=True)
  40. if os.path.isdir(self.pip_temp):
  41. shutil.rmtree(self.pip_temp, ignore_errors=True)
  42. del self.venv_dir
  43. del self.venv_test_dir
  44. del self.pip_temp
  45. if 'PIP_SOURCE_DIR' in os.environ:
  46. os.environ.pop('PIP_SOURCE_DIR')
  47. if 'PIP_BUILD_DIR' in os.environ:
  48. os.environ.pop('PIP_BUILD_DIR')
  49. def _check_download_error(self, ret):
  50. '''
  51. Checks to see if a download error looks transitory
  52. '''
  53. return any(w in ret for w in ['URLError', 'Download error'])
  54. def pip_successful_install(self, target, expect=('irc3-plugins-test', 'pep8',)):
  55. '''
  56. isolate regex for extracting `successful install` message from pip
  57. '''
  58. expect = set(expect)
  59. expect_str = '|'.join(expect)
  60. success = re.search(
  61. r'^.*Successfully installed\s([^\n]+)(?:Clean.*)?',
  62. target,
  63. re.M | re.S)
  64. success_for = re.findall(
  65. r'({0})(?:-(?:[\d\.-]))?'.format(expect_str),
  66. success.groups()[0]
  67. ) if success else []
  68. return expect.issubset(set(success_for))
  69. def test_issue_2087_missing_pip(self):
  70. # Let's create the testing virtualenv
  71. self.run_function('virtualenv.create', [self.venv_dir])
  72. # Let's remove the pip binary
  73. pip_bin = os.path.join(self.venv_dir, 'bin', 'pip')
  74. site_dir = self.run_function('virtualenv.get_distribution_path', [self.venv_dir, 'pip'])
  75. if salt.utils.platform.is_windows():
  76. pip_bin = os.path.join(self.venv_dir, 'Scripts', 'pip.exe')
  77. site_dir = os.path.join(self.venv_dir, 'lib', 'site-packages')
  78. if not os.path.isfile(pip_bin):
  79. self.skipTest(
  80. 'Failed to find the pip binary to the test virtualenv'
  81. )
  82. os.remove(pip_bin)
  83. # Also remove the pip dir from site-packages
  84. # This is needed now that we're using python -m pip instead of the
  85. # pip binary directly. python -m pip will still work even if the
  86. # pip binary is missing
  87. shutil.rmtree(os.path.join(site_dir, 'pip'))
  88. # Let's run a pip depending functions
  89. for func in ('pip.freeze', 'pip.list'):
  90. ret = self.run_function(func, bin_env=self.venv_dir)
  91. self.assertIn(
  92. 'Command required for \'{0}\' not found: '
  93. 'Could not find a `pip` binary'.format(func),
  94. ret
  95. )
  96. @skip_if_not_root
  97. def test_requirements_as_list_of_chains__cwd_set__absolute_file_path(self):
  98. self.run_function('virtualenv.create', [self.venv_dir])
  99. # Create a requirements file that depends on another one.
  100. req1_filename = os.path.join(self.venv_dir, 'requirements1.txt')
  101. req1b_filename = os.path.join(self.venv_dir, 'requirements1b.txt')
  102. req2_filename = os.path.join(self.venv_dir, 'requirements2.txt')
  103. req2b_filename = os.path.join(self.venv_dir, 'requirements2b.txt')
  104. with salt.utils.files.fopen(req1_filename, 'w') as f:
  105. f.write('-r requirements1b.txt\n')
  106. with salt.utils.files.fopen(req1b_filename, 'w') as f:
  107. f.write('irc3-plugins-test\n')
  108. with salt.utils.files.fopen(req2_filename, 'w') as f:
  109. f.write('-r requirements2b.txt\n')
  110. with salt.utils.files.fopen(req2b_filename, 'w') as f:
  111. f.write('pep8\n')
  112. requirements_list = [req1_filename, req2_filename]
  113. ret = self.run_function(
  114. 'pip.install', requirements=requirements_list,
  115. bin_env=self.venv_dir, cwd=self.venv_dir
  116. )
  117. try:
  118. self.assertEqual(ret['retcode'], 0)
  119. found = self.pip_successful_install(ret['stdout'])
  120. self.assertTrue(found)
  121. except (AssertionError, TypeError):
  122. import pprint
  123. pprint.pprint(ret)
  124. raise
  125. @skip_if_not_root
  126. def test_requirements_as_list_of_chains__cwd_not_set__absolute_file_path(self):
  127. self.run_function('virtualenv.create', [self.venv_dir])
  128. # Create a requirements file that depends on another one.
  129. req1_filename = os.path.join(self.venv_dir, 'requirements1.txt')
  130. req1b_filename = os.path.join(self.venv_dir, 'requirements1b.txt')
  131. req2_filename = os.path.join(self.venv_dir, 'requirements2.txt')
  132. req2b_filename = os.path.join(self.venv_dir, 'requirements2b.txt')
  133. with salt.utils.files.fopen(req1_filename, 'w') as f:
  134. f.write('-r requirements1b.txt\n')
  135. with salt.utils.files.fopen(req1b_filename, 'w') as f:
  136. f.write('irc3-plugins-test\n')
  137. with salt.utils.files.fopen(req2_filename, 'w') as f:
  138. f.write('-r requirements2b.txt\n')
  139. with salt.utils.files.fopen(req2b_filename, 'w') as f:
  140. f.write('pep8\n')
  141. requirements_list = [req1_filename, req2_filename]
  142. ret = self.run_function(
  143. 'pip.install', requirements=requirements_list, bin_env=self.venv_dir
  144. )
  145. try:
  146. self.assertEqual(ret['retcode'], 0)
  147. found = self.pip_successful_install(ret['stdout'])
  148. self.assertTrue(found)
  149. except (AssertionError, TypeError):
  150. import pprint
  151. pprint.pprint(ret)
  152. raise
  153. @skip_if_not_root
  154. def test_requirements_as_list__absolute_file_path(self):
  155. self.run_function('virtualenv.create', [self.venv_dir])
  156. req1_filename = os.path.join(self.venv_dir, 'requirements.txt')
  157. req2_filename = os.path.join(self.venv_dir, 'requirements2.txt')
  158. with salt.utils.files.fopen(req1_filename, 'w') as f:
  159. f.write('irc3-plugins-test\n')
  160. with salt.utils.files.fopen(req2_filename, 'w') as f:
  161. f.write('pep8\n')
  162. requirements_list = [req1_filename, req2_filename]
  163. ret = self.run_function(
  164. 'pip.install', requirements=requirements_list, bin_env=self.venv_dir
  165. )
  166. found = self.pip_successful_install(ret['stdout'])
  167. try:
  168. self.assertEqual(ret['retcode'], 0)
  169. self.assertTrue(found)
  170. except (AssertionError, TypeError):
  171. import pprint
  172. pprint.pprint(ret)
  173. raise
  174. @skip_if_not_root
  175. def test_requirements_as_list__non_absolute_file_path(self):
  176. self.run_function('virtualenv.create', [self.venv_dir])
  177. # Create a requirements file that depends on another one.
  178. req1_filename = 'requirements.txt'
  179. req2_filename = 'requirements2.txt'
  180. req_cwd = self.venv_dir
  181. req1_filepath = os.path.join(req_cwd, req1_filename)
  182. req2_filepath = os.path.join(req_cwd, req2_filename)
  183. with salt.utils.files.fopen(req1_filepath, 'w') as f:
  184. f.write('irc3-plugins-test\n')
  185. with salt.utils.files.fopen(req2_filepath, 'w') as f:
  186. f.write('pep8\n')
  187. requirements_list = [req1_filename, req2_filename]
  188. ret = self.run_function(
  189. 'pip.install', requirements=requirements_list,
  190. bin_env=self.venv_dir, cwd=req_cwd
  191. )
  192. try:
  193. self.assertEqual(ret['retcode'], 0)
  194. found = self.pip_successful_install(ret['stdout'])
  195. self.assertTrue(found)
  196. except (AssertionError, TypeError):
  197. import pprint
  198. pprint.pprint(ret)
  199. raise
  200. @skip_if_not_root
  201. def test_chained_requirements__absolute_file_path(self):
  202. self.run_function('virtualenv.create', [self.venv_dir])
  203. # Create a requirements file that depends on another one.
  204. req1_filename = os.path.join(self.venv_dir, 'requirements.txt')
  205. req2_filename = os.path.join(self.venv_dir, 'requirements2.txt')
  206. with salt.utils.files.fopen(req1_filename, 'w') as f:
  207. f.write('-r requirements2.txt')
  208. with salt.utils.files.fopen(req2_filename, 'w') as f:
  209. f.write('pep8')
  210. ret = self.run_function(
  211. 'pip.install', requirements=req1_filename, bin_env=self.venv_dir
  212. )
  213. try:
  214. self.assertEqual(ret['retcode'], 0)
  215. self.assertIn('installed pep8', ret['stdout'])
  216. except (AssertionError, TypeError):
  217. import pprint
  218. pprint.pprint(ret)
  219. raise
  220. @skip_if_not_root
  221. def test_chained_requirements__non_absolute_file_path(self):
  222. self.run_function('virtualenv.create', [self.venv_dir])
  223. # Create a requirements file that depends on another one.
  224. req_basepath = (self.venv_dir)
  225. req1_filename = 'requirements.txt'
  226. req2_filename = 'requirements2.txt'
  227. req1_file = os.path.join(self.venv_dir, req1_filename)
  228. req2_file = os.path.join(self.venv_dir, req2_filename)
  229. with salt.utils.files.fopen(req1_file, 'w') as f:
  230. f.write('-r requirements2.txt')
  231. with salt.utils.files.fopen(req2_file, 'w') as f:
  232. f.write('pep8')
  233. ret = self.run_function(
  234. 'pip.install', requirements=req1_filename, cwd=req_basepath,
  235. bin_env=self.venv_dir
  236. )
  237. try:
  238. self.assertEqual(ret['retcode'], 0)
  239. self.assertIn('installed pep8', ret['stdout'])
  240. except (AssertionError, TypeError):
  241. import pprint
  242. pprint.pprint(ret)
  243. raise
  244. @skip_if_not_root
  245. def test_issue_4805_nested_requirements(self):
  246. self.run_function('virtualenv.create', [self.venv_dir])
  247. # Create a requirements file that depends on another one.
  248. req1_filename = os.path.join(self.venv_dir, 'requirements.txt')
  249. req2_filename = os.path.join(self.venv_dir, 'requirements2.txt')
  250. with salt.utils.files.fopen(req1_filename, 'w') as f:
  251. f.write('-r requirements2.txt')
  252. with salt.utils.files.fopen(req2_filename, 'w') as f:
  253. f.write('pep8')
  254. ret = self.run_function(
  255. 'pip.install', requirements=req1_filename, bin_env=self.venv_dir, timeout=300)
  256. if self._check_download_error(ret['stdout']):
  257. self.skipTest('Test skipped due to pip download error')
  258. try:
  259. self.assertEqual(ret['retcode'], 0)
  260. self.assertIn('installed pep8', ret['stdout'])
  261. except (AssertionError, TypeError):
  262. import pprint
  263. pprint.pprint(ret)
  264. raise
  265. def test_pip_uninstall(self):
  266. # Let's create the testing virtualenv
  267. self.run_function('virtualenv.create', [self.venv_dir])
  268. ret = self.run_function('pip.install', ['pep8'], bin_env=self.venv_dir)
  269. if self._check_download_error(ret['stdout']):
  270. self.skipTest('Test skipped due to pip download error')
  271. self.assertEqual(ret['retcode'], 0)
  272. self.assertIn('installed pep8', ret['stdout'])
  273. ret = self.run_function(
  274. 'pip.uninstall', ['pep8'], bin_env=self.venv_dir
  275. )
  276. try:
  277. self.assertEqual(ret['retcode'], 0)
  278. self.assertIn('uninstalled pep8', ret['stdout'])
  279. except AssertionError:
  280. import pprint
  281. pprint.pprint(ret)
  282. raise
  283. def test_pip_install_upgrade(self):
  284. # Create the testing virtualenv
  285. self.run_function('virtualenv.create', [self.venv_dir])
  286. ret = self.run_function(
  287. 'pip.install', ['pep8==1.3.4'], bin_env=self.venv_dir
  288. )
  289. if self._check_download_error(ret['stdout']):
  290. self.skipTest('Test skipped due to pip download error')
  291. try:
  292. self.assertEqual(ret['retcode'], 0)
  293. self.assertIn('installed pep8', ret['stdout'])
  294. except AssertionError:
  295. import pprint
  296. pprint.pprint(ret)
  297. raise
  298. ret = self.run_function(
  299. 'pip.install',
  300. ['pep8'],
  301. bin_env=self.venv_dir,
  302. upgrade=True
  303. )
  304. if self._check_download_error(ret['stdout']):
  305. self.skipTest('Test skipped due to pip download error')
  306. try:
  307. self.assertEqual(ret['retcode'], 0)
  308. self.assertIn('installed pep8', ret['stdout'])
  309. except AssertionError:
  310. import pprint
  311. pprint.pprint(ret)
  312. raise
  313. ret = self.run_function(
  314. 'pip.uninstall', ['pep8'], bin_env=self.venv_dir
  315. )
  316. try:
  317. self.assertEqual(ret['retcode'], 0)
  318. self.assertIn('uninstalled pep8', ret['stdout'])
  319. except AssertionError:
  320. import pprint
  321. pprint.pprint(ret)
  322. raise
  323. def test_pip_install_multiple_editables(self):
  324. editables = [
  325. 'git+https://github.com/jek/blinker.git#egg=Blinker',
  326. 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting'
  327. ]
  328. # Create the testing virtualenv
  329. self.run_function('virtualenv.create', [self.venv_dir])
  330. ret = self.run_function(
  331. 'pip.install', [],
  332. editable='{0}'.format(','.join(editables)),
  333. bin_env=self.venv_dir
  334. )
  335. if self._check_download_error(ret['stdout']):
  336. self.skipTest('Test skipped due to pip download error')
  337. try:
  338. self.assertEqual(ret['retcode'], 0)
  339. self.assertIn(
  340. 'Successfully installed Blinker SaltTesting', ret['stdout']
  341. )
  342. except AssertionError:
  343. import pprint
  344. pprint.pprint(ret)
  345. raise
  346. def test_pip_install_multiple_editables_and_pkgs(self):
  347. editables = [
  348. 'git+https://github.com/jek/blinker.git#egg=Blinker',
  349. 'git+https://github.com/saltstack/salt-testing.git#egg=SaltTesting'
  350. ]
  351. # Create the testing virtualenv
  352. self.run_function('virtualenv.create', [self.venv_dir])
  353. ret = self.run_function(
  354. 'pip.install', ['pep8'],
  355. editable='{0}'.format(','.join(editables)),
  356. bin_env=self.venv_dir
  357. )
  358. if self._check_download_error(ret['stdout']):
  359. self.skipTest('Test skipped due to pip download error')
  360. try:
  361. self.assertEqual(ret['retcode'], 0)
  362. for package in ('Blinker', 'SaltTesting', 'pep8'):
  363. self.assertRegex(
  364. ret['stdout'],
  365. r'(?:.*)(Successfully installed)(?:.*)({0})(?:.*)'.format(package)
  366. )
  367. except AssertionError:
  368. import pprint
  369. pprint.pprint(ret)
  370. raise
  371. @skipIf(not os.path.isfile('pip3'), 'test where pip3 is installed')
  372. @skipIf(salt.utils.platform.is_windows(), 'test specific for linux usage of /bin/python')
  373. def test_system_pip3(self):
  374. self.run_function('pip.install', pkgs=['lazyimport==0.0.1'], bin_env='/bin/pip3')
  375. ret1 = self.run_function('cmd.run', '/bin/pip3 freeze | grep lazyimport')
  376. self.run_function('pip.uninstall', pkgs=['lazyimport'], bin_env='/bin/pip3')
  377. ret2 = self.run_function('cmd.run', '/bin/pip3 freeze | grep lazyimport')
  378. assert 'lazyimport==0.0.1' in ret1
  379. assert ret2 == ''