test_archive.py 19 KB

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