test_archive.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. # -*- coding: utf-8 -*-
  2. """
  3. :codeauthor: Pedro Algarvio (pedro@algarvio.me)
  4. tests.unit.modules.archive_test
  5. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  6. """
  7. # Import Python libs
  8. from __future__ import absolute_import, print_function, unicode_literals
  9. import os.path
  10. # Import salt libs
  11. import salt.modules.archive as archive
  12. import salt.utils.path
  13. from salt.exceptions import CommandNotFoundError
  14. # Import 3rd-party libs
  15. from salt.ext.six.moves import zip
  16. # Import Salt Testing libs
  17. from tests.support.mixins import LoaderModuleMockMixin
  18. from tests.support.mock import MagicMock, patch
  19. from tests.support.unit import TestCase, skipIf
  20. class ZipFileMock(MagicMock):
  21. def __init__(self, files=None, **kwargs): # pylint: disable=W0231
  22. if files is None:
  23. files = ["salt"]
  24. MagicMock.__init__(self, **kwargs)
  25. self._files = files
  26. self.external_attr = 0o0777 << 16
  27. def namelist(self):
  28. return self._files
  29. class ArchiveTestCase(TestCase, LoaderModuleMockMixin):
  30. def setup_loader_modules(self):
  31. return {archive: {"__grains__": {"id": 0}}}
  32. def test_tar(self):
  33. with patch("glob.glob", lambda pathname: [pathname]):
  34. with patch("salt.utils.path.which", lambda exe: exe):
  35. mock = MagicMock(return_value="salt")
  36. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  37. ret = archive.tar(
  38. "-zcvf",
  39. "foo.tar",
  40. [
  41. "/tmp/something-to-compress-1",
  42. "/tmp/something-to-compress-2",
  43. ],
  44. template=None,
  45. )
  46. self.assertEqual(["salt"], ret)
  47. mock.assert_called_once_with(
  48. [
  49. "tar",
  50. "-zcvf",
  51. "foo.tar",
  52. "/tmp/something-to-compress-1",
  53. "/tmp/something-to-compress-2",
  54. ],
  55. runas=None,
  56. python_shell=False,
  57. template=None,
  58. cwd=None,
  59. )
  60. mock = MagicMock(return_value="salt")
  61. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  62. ret = archive.tar(
  63. "-zcvf",
  64. "foo.tar",
  65. "/tmp/something-to-compress-1,/tmp/something-to-compress-2",
  66. template=None,
  67. )
  68. self.assertEqual(["salt"], ret)
  69. mock.assert_called_once_with(
  70. [
  71. "tar",
  72. "-zcvf",
  73. "foo.tar",
  74. "/tmp/something-to-compress-1",
  75. "/tmp/something-to-compress-2",
  76. ],
  77. runas=None,
  78. python_shell=False,
  79. template=None,
  80. cwd=None,
  81. )
  82. def test_tar_raises_exception_if_not_found(self):
  83. with patch("salt.utils.path.which", lambda exe: None):
  84. mock = MagicMock(return_value="salt")
  85. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  86. self.assertRaises(
  87. CommandNotFoundError,
  88. archive.tar,
  89. "zxvf",
  90. "foo.tar",
  91. "/tmp/something-to-compress",
  92. )
  93. self.assertFalse(mock.called)
  94. def test_gzip(self):
  95. mock = MagicMock(return_value="salt")
  96. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  97. with patch("salt.utils.path.which", lambda exe: exe):
  98. ret = archive.gzip("/tmp/something-to-compress")
  99. self.assertEqual(["salt"], ret)
  100. mock.assert_called_once_with(
  101. ["gzip", "/tmp/something-to-compress"],
  102. runas=None,
  103. python_shell=False,
  104. template=None,
  105. )
  106. def test_gzip_raises_exception_if_not_found(self):
  107. mock = MagicMock(return_value="salt")
  108. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  109. with patch("salt.utils.path.which", lambda exe: None):
  110. self.assertRaises(
  111. CommandNotFoundError, archive.gzip, "/tmp/something-to-compress"
  112. )
  113. self.assertFalse(mock.called)
  114. def test_gunzip(self):
  115. mock = MagicMock(return_value="salt")
  116. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  117. with patch("salt.utils.path.which", lambda exe: exe):
  118. ret = archive.gunzip("/tmp/something-to-decompress.tar.gz")
  119. self.assertEqual(["salt"], ret)
  120. mock.assert_called_once_with(
  121. ["gunzip", "/tmp/something-to-decompress.tar.gz"],
  122. runas=None,
  123. python_shell=False,
  124. template=None,
  125. )
  126. def test_gunzip_raises_exception_if_not_found(self):
  127. mock = MagicMock(return_value="salt")
  128. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  129. with patch("salt.utils.path.which", lambda exe: None):
  130. self.assertRaises(
  131. CommandNotFoundError,
  132. archive.gunzip,
  133. "/tmp/something-to-decompress.tar.gz",
  134. )
  135. self.assertFalse(mock.called)
  136. def test_cmd_zip(self):
  137. with patch("glob.glob", lambda pathname: [pathname]):
  138. with patch("salt.utils.path.which", lambda exe: exe):
  139. mock = MagicMock(return_value="salt")
  140. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  141. ret = archive.cmd_zip(
  142. "/tmp/salt.{{grains.id}}.zip",
  143. "/tmp/tmpePe8yO,/tmp/tmpLeSw1A",
  144. template="jinja",
  145. )
  146. self.assertEqual(["salt"], ret)
  147. mock.assert_called_once_with(
  148. [
  149. "zip",
  150. "-r",
  151. "/tmp/salt.{{grains.id}}.zip",
  152. "/tmp/tmpePe8yO",
  153. "/tmp/tmpLeSw1A",
  154. ],
  155. runas=None,
  156. python_shell=False,
  157. template="jinja",
  158. cwd=None,
  159. )
  160. mock = MagicMock(return_value="salt")
  161. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  162. ret = archive.cmd_zip(
  163. "/tmp/salt.{{grains.id}}.zip",
  164. ["/tmp/tmpePe8yO", "/tmp/tmpLeSw1A"],
  165. template="jinja",
  166. )
  167. self.assertEqual(["salt"], ret)
  168. mock.assert_called_once_with(
  169. [
  170. "zip",
  171. "-r",
  172. "/tmp/salt.{{grains.id}}.zip",
  173. "/tmp/tmpePe8yO",
  174. "/tmp/tmpLeSw1A",
  175. ],
  176. runas=None,
  177. python_shell=False,
  178. template="jinja",
  179. cwd=None,
  180. )
  181. def test_zip(self):
  182. with patch("glob.glob", lambda pathname: [pathname]):
  183. with patch.multiple(
  184. os.path,
  185. **{
  186. "isdir": MagicMock(return_value=False),
  187. "exists": MagicMock(return_value=True),
  188. }
  189. ):
  190. with patch("zipfile.ZipFile", MagicMock()):
  191. ret = archive.zip_(
  192. "/tmp/salt.{{grains.id}}.zip",
  193. "/tmp/tmpePe8yO,/tmp/tmpLeSw1A",
  194. template="jinja",
  195. )
  196. expected = [
  197. os.path.join("tmp", "tmpePe8yO"),
  198. os.path.join("tmp", "tmpLeSw1A"),
  199. ]
  200. self.assertEqual(expected, ret)
  201. def test_zip_raises_exception_if_not_found(self):
  202. mock = MagicMock(return_value="salt")
  203. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  204. with patch("salt.utils.path.which", lambda exe: None):
  205. self.assertRaises(
  206. CommandNotFoundError,
  207. archive.cmd_zip,
  208. "/tmp/salt.{{grains.id}}.zip",
  209. "/tmp/tmpePe8yO,/tmp/tmpLeSw1A",
  210. template="jinja",
  211. )
  212. self.assertFalse(mock.called)
  213. def test_cmd_unzip(self):
  214. def _get_mock():
  215. """
  216. Create a new MagicMock for each scenario in this test, so that
  217. assert_called_once_with doesn't complain that the same mock object
  218. is called more than once.
  219. """
  220. return MagicMock(
  221. return_value={
  222. "stdout": "salt",
  223. "stderr": "",
  224. "pid": 12345,
  225. "retcode": 0,
  226. }
  227. )
  228. with patch("salt.utils.path.which", lambda exe: exe):
  229. mock = _get_mock()
  230. with patch.dict(archive.__salt__, {"cmd.run_all": mock}):
  231. ret = archive.cmd_unzip(
  232. "/tmp/salt.{{grains.id}}.zip",
  233. "/tmp/dest",
  234. excludes="/tmp/tmpePe8yO,/tmp/tmpLeSw1A",
  235. template="jinja",
  236. )
  237. self.assertEqual(["salt"], ret)
  238. mock.assert_called_once_with(
  239. [
  240. "unzip",
  241. "/tmp/salt.{{grains.id}}.zip",
  242. "-d",
  243. "/tmp/dest",
  244. "-x",
  245. "/tmp/tmpePe8yO",
  246. "/tmp/tmpLeSw1A",
  247. ],
  248. output_loglevel="debug",
  249. python_shell=False,
  250. redirect_stderr=True,
  251. runas=None,
  252. template="jinja",
  253. )
  254. mock = _get_mock()
  255. with patch.dict(archive.__salt__, {"cmd.run_all": mock}):
  256. ret = archive.cmd_unzip(
  257. "/tmp/salt.{{grains.id}}.zip",
  258. "/tmp/dest",
  259. excludes=["/tmp/tmpePe8yO", "/tmp/tmpLeSw1A"],
  260. template="jinja",
  261. )
  262. self.assertEqual(["salt"], ret)
  263. mock.assert_called_once_with(
  264. [
  265. "unzip",
  266. "/tmp/salt.{{grains.id}}.zip",
  267. "-d",
  268. "/tmp/dest",
  269. "-x",
  270. "/tmp/tmpePe8yO",
  271. "/tmp/tmpLeSw1A",
  272. ],
  273. output_loglevel="debug",
  274. python_shell=False,
  275. redirect_stderr=True,
  276. runas=None,
  277. template="jinja",
  278. )
  279. mock = _get_mock()
  280. with patch.dict(archive.__salt__, {"cmd.run_all": mock}):
  281. ret = archive.cmd_unzip(
  282. "/tmp/salt.{{grains.id}}.zip",
  283. "/tmp/dest",
  284. excludes="/tmp/tmpePe8yO,/tmp/tmpLeSw1A",
  285. template="jinja",
  286. options="-fo",
  287. )
  288. self.assertEqual(["salt"], ret)
  289. mock.assert_called_once_with(
  290. [
  291. "unzip",
  292. "-fo",
  293. "/tmp/salt.{{grains.id}}.zip",
  294. "-d",
  295. "/tmp/dest",
  296. "-x",
  297. "/tmp/tmpePe8yO",
  298. "/tmp/tmpLeSw1A",
  299. ],
  300. output_loglevel="debug",
  301. python_shell=False,
  302. redirect_stderr=True,
  303. runas=None,
  304. template="jinja",
  305. )
  306. mock = _get_mock()
  307. with patch.dict(archive.__salt__, {"cmd.run_all": mock}):
  308. ret = archive.cmd_unzip(
  309. "/tmp/salt.{{grains.id}}.zip",
  310. "/tmp/dest",
  311. excludes=["/tmp/tmpePe8yO", "/tmp/tmpLeSw1A"],
  312. template="jinja",
  313. options="-fo",
  314. )
  315. self.assertEqual(["salt"], ret)
  316. mock.assert_called_once_with(
  317. [
  318. "unzip",
  319. "-fo",
  320. "/tmp/salt.{{grains.id}}.zip",
  321. "-d",
  322. "/tmp/dest",
  323. "-x",
  324. "/tmp/tmpePe8yO",
  325. "/tmp/tmpLeSw1A",
  326. ],
  327. output_loglevel="debug",
  328. python_shell=False,
  329. redirect_stderr=True,
  330. runas=None,
  331. template="jinja",
  332. )
  333. mock = _get_mock()
  334. with patch.dict(archive.__salt__, {"cmd.run_all": mock}):
  335. ret = archive.cmd_unzip(
  336. "/tmp/salt.{{grains.id}}.zip",
  337. "/tmp/dest",
  338. excludes=["/tmp/tmpePe8yO", "/tmp/tmpLeSw1A"],
  339. template="jinja",
  340. options="-fo",
  341. password="asdf",
  342. )
  343. self.assertEqual(["salt"], ret)
  344. mock.assert_called_once_with(
  345. [
  346. "unzip",
  347. "-P",
  348. "asdf",
  349. "-fo",
  350. "/tmp/salt.{{grains.id}}.zip",
  351. "-d",
  352. "/tmp/dest",
  353. "-x",
  354. "/tmp/tmpePe8yO",
  355. "/tmp/tmpLeSw1A",
  356. ],
  357. output_loglevel="quiet",
  358. python_shell=False,
  359. redirect_stderr=True,
  360. runas=None,
  361. template="jinja",
  362. )
  363. def test_unzip(self):
  364. mock = ZipFileMock()
  365. with patch("zipfile.ZipFile", mock):
  366. ret = archive.unzip(
  367. "/tmp/salt.{{grains.id}}.zip",
  368. "/tmp/dest",
  369. excludes="/tmp/tmpePe8yO,/tmp/tmpLeSw1A",
  370. template="jinja",
  371. extract_perms=False,
  372. )
  373. self.assertEqual(["salt"], ret)
  374. def test_unzip_raises_exception_if_not_found(self):
  375. mock = MagicMock(return_value="salt")
  376. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  377. with patch("salt.utils.path.which", lambda exe: None):
  378. self.assertRaises(
  379. CommandNotFoundError,
  380. archive.cmd_unzip,
  381. "/tmp/salt.{{grains.id}}.zip",
  382. "/tmp/dest",
  383. excludes="/tmp/tmpePe8yO,/tmp/tmpLeSw1A",
  384. template="jinja",
  385. )
  386. self.assertFalse(mock.called)
  387. def test_rar(self):
  388. with patch("glob.glob", lambda pathname: [pathname]):
  389. with patch("salt.utils.path.which", lambda exe: exe):
  390. mock = MagicMock(return_value="salt")
  391. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  392. ret = archive.rar(
  393. "/tmp/rarfile.rar", "/tmp/sourcefile1,/tmp/sourcefile2"
  394. )
  395. self.assertEqual(["salt"], ret)
  396. mock.assert_called_once_with(
  397. [
  398. "rar",
  399. "a",
  400. "-idp",
  401. "/tmp/rarfile.rar",
  402. "/tmp/sourcefile1",
  403. "/tmp/sourcefile2",
  404. ],
  405. runas=None,
  406. python_shell=False,
  407. template=None,
  408. cwd=None,
  409. )
  410. mock = MagicMock(return_value="salt")
  411. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  412. ret = archive.rar(
  413. "/tmp/rarfile.rar", ["/tmp/sourcefile1", "/tmp/sourcefile2"]
  414. )
  415. self.assertEqual(["salt"], ret)
  416. mock.assert_called_once_with(
  417. [
  418. "rar",
  419. "a",
  420. "-idp",
  421. "/tmp/rarfile.rar",
  422. "/tmp/sourcefile1",
  423. "/tmp/sourcefile2",
  424. ],
  425. runas=None,
  426. python_shell=False,
  427. template=None,
  428. cwd=None,
  429. )
  430. def test_rar_raises_exception_if_not_found(self):
  431. mock = MagicMock(return_value="salt")
  432. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  433. with patch("salt.utils.path.which", lambda exe: None):
  434. self.assertRaises(
  435. CommandNotFoundError,
  436. archive.rar,
  437. "/tmp/rarfile.rar",
  438. "/tmp/sourcefile1,/tmp/sourcefile2",
  439. )
  440. self.assertFalse(mock.called)
  441. @skipIf(salt.utils.path.which_bin(("unrar", "rar")) is None, "unrar not installed")
  442. def test_unrar(self):
  443. with patch(
  444. "salt.utils.path.which_bin",
  445. lambda exe: exe[0] if isinstance(exe, (list, tuple)) else exe,
  446. ):
  447. with patch(
  448. "salt.utils.path.which",
  449. lambda exe: exe[0] if isinstance(exe, (list, tuple)) else exe,
  450. ):
  451. mock = MagicMock(return_value="salt")
  452. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  453. ret = archive.unrar(
  454. "/tmp/rarfile.rar", "/home/strongbad/", excludes="file_1,file_2"
  455. )
  456. self.assertEqual(["salt"], ret)
  457. mock.assert_called_once_with(
  458. [
  459. "unrar",
  460. "x",
  461. "-idp",
  462. "/tmp/rarfile.rar",
  463. "-x",
  464. "file_1",
  465. "-x",
  466. "file_2",
  467. "/home/strongbad/",
  468. ],
  469. runas=None,
  470. python_shell=False,
  471. template=None,
  472. )
  473. mock = MagicMock(return_value="salt")
  474. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  475. ret = archive.unrar(
  476. "/tmp/rarfile.rar",
  477. "/home/strongbad/",
  478. excludes=["file_1", "file_2"],
  479. )
  480. self.assertEqual(["salt"], ret)
  481. mock.assert_called_once_with(
  482. [
  483. "unrar",
  484. "x",
  485. "-idp",
  486. "/tmp/rarfile.rar",
  487. "-x",
  488. "file_1",
  489. "-x",
  490. "file_2",
  491. "/home/strongbad/",
  492. ],
  493. runas=None,
  494. python_shell=False,
  495. template=None,
  496. )
  497. def test_unrar_raises_exception_if_not_found(self):
  498. with patch("salt.utils.path.which_bin", lambda exe: None):
  499. mock = MagicMock(return_value="salt")
  500. with patch.dict(archive.__salt__, {"cmd.run": mock}):
  501. self.assertRaises(
  502. CommandNotFoundError,
  503. archive.unrar,
  504. "/tmp/rarfile.rar",
  505. "/home/strongbad/",
  506. )
  507. self.assertFalse(mock.called)
  508. def test_trim_files(self):
  509. with patch("salt.utils.path.which_bin", lambda exe: exe):
  510. source = "file.tar.gz"
  511. tmp_dir = "temp"
  512. files = [
  513. "\n".join(["file1"] * 200),
  514. "\n".join(["file2"] * 200),
  515. "this\nthat\nother",
  516. "this\nthat\nother",
  517. "this\nthat\nother",
  518. ]
  519. trim_opt = [True, False, 3, 1, 0]
  520. trim_100 = ["file1"] * 100
  521. trim_100.append("List trimmed after 100 files.")
  522. expected = [
  523. trim_100,
  524. ["file2"] * 200,
  525. ["this", "that", "other"],
  526. ["this", "List trimmed after 1 files."],
  527. ["List trimmed after 0 files."],
  528. ]
  529. for test_files, test_trim_opts, test_expected in zip(
  530. files, trim_opt, expected
  531. ):
  532. with patch.dict(
  533. archive.__salt__, {"cmd.run": MagicMock(return_value=test_files)}
  534. ):
  535. ret = archive.unrar(source, tmp_dir, trim_output=test_trim_opts)
  536. self.assertEqual(ret, test_expected)