gitfs.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747
  1. """
  2. Base classes for gitfs/git_pillar integration tests
  3. """
  4. import errno
  5. import logging
  6. import os
  7. import shutil
  8. import tempfile
  9. import textwrap
  10. import attr
  11. import pytest
  12. import salt.utils.files
  13. import salt.utils.path
  14. import salt.utils.platform
  15. import salt.utils.yaml
  16. from salt.fileserver import gitfs
  17. from salt.pillar import git_pillar
  18. from salt.utils.immutabletypes import freeze
  19. from saltfactories.factories.base import DaemonFactory
  20. from saltfactories.factories.daemons.sshd import SshdDaemonFactory as _SshdDaemonFactory
  21. from saltfactories.utils.ports import get_unused_localhost_port
  22. from tests.support.case import ModuleCase
  23. from tests.support.helpers import (
  24. SKIP_IF_NOT_RUNNING_PYTEST,
  25. patched_environ,
  26. requires_system_grains,
  27. )
  28. from tests.support.mixins import LoaderModuleMockMixin
  29. from tests.support.mock import patch
  30. from tests.support.runtests import RUNTIME_VARS
  31. log = logging.getLogger(__name__)
  32. USERNAME = "gitpillaruser"
  33. PASSWORD = "saltrules"
  34. _OPTS = freeze(
  35. {
  36. "__role": "minion",
  37. "environment": None,
  38. "pillarenv": None,
  39. "hash_type": "sha256",
  40. "file_roots": {},
  41. "state_top": "top.sls",
  42. "state_top_saltenv": None,
  43. "renderer": "yaml_jinja",
  44. "renderer_whitelist": [],
  45. "renderer_blacklist": [],
  46. "pillar_merge_lists": False,
  47. "git_pillar_base": "master",
  48. "git_pillar_branch": "master",
  49. "git_pillar_env": "",
  50. "git_pillar_fallback": "",
  51. "git_pillar_root": "",
  52. "git_pillar_ssl_verify": True,
  53. "git_pillar_global_lock": True,
  54. "git_pillar_user": "",
  55. "git_pillar_password": "",
  56. "git_pillar_insecure_auth": False,
  57. "git_pillar_privkey": "",
  58. "git_pillar_pubkey": "",
  59. "git_pillar_passphrase": "",
  60. "git_pillar_refspecs": [
  61. "+refs/heads/*:refs/remotes/origin/*",
  62. "+refs/tags/*:refs/tags/*",
  63. ],
  64. "git_pillar_includes": True,
  65. }
  66. )
  67. class SshdDaemonFactory(_SshdDaemonFactory):
  68. def apply_pre_start_states(self, salt_call_cli, testclass, username):
  69. if self.listen_port in self.check_ports:
  70. self.check_ports.remove(self.listen_port)
  71. if self.listen_port in self.listen_ports:
  72. self.listen_ports.remove(self.listen_port)
  73. self.listen_port = get_unused_localhost_port()
  74. self.check_ports.append(self.listen_port)
  75. self.listen_ports.append(self.listen_port)
  76. url = "ssh://{username}@127.0.0.1:{port}/~/repo.git".format(
  77. username=testclass.username, port=self.listen_port
  78. )
  79. url_extra_repo = "ssh://{username}@127.0.0.1:{port}/~/extra_repo.git".format(
  80. username=testclass.username, port=self.listen_port
  81. )
  82. home = "/root/.ssh"
  83. testclass.ext_opts = {
  84. "url": url,
  85. "url_extra_repo": url_extra_repo,
  86. "privkey_nopass": os.path.join(home, testclass.id_rsa_nopass),
  87. "pubkey_nopass": os.path.join(home, testclass.id_rsa_nopass + ".pub"),
  88. "privkey_withpass": os.path.join(home, testclass.id_rsa_withpass),
  89. "pubkey_withpass": os.path.join(home, testclass.id_rsa_withpass + ".pub"),
  90. "passphrase": testclass.passphrase,
  91. }
  92. ret = salt_call_cli.run(
  93. "state.apply",
  94. mods="git_pillar.ssh",
  95. pillar={
  96. "git_pillar": {
  97. "git_ssh": testclass.git_ssh,
  98. "id_rsa_nopass": testclass.id_rsa_nopass,
  99. "id_rsa_withpass": testclass.id_rsa_withpass,
  100. "sshd_bin": self.get_script_path(),
  101. "sshd_port": self.listen_port,
  102. "sshd_config_dir": str(self.config_dir),
  103. "master_user": username,
  104. "user": testclass.username,
  105. }
  106. },
  107. _timeout=240,
  108. )
  109. if ret.exitcode != 0:
  110. pytest.fail("Failed to apply the 'git_pillar.ssh' state")
  111. if next(iter(ret.json.values()))["result"] is not True:
  112. pytest.fail("Failed to apply the 'git_pillar.ssh' state")
  113. def set_known_host(self, salt_call_cli, username):
  114. ret = salt_call_cli.run(
  115. "ssh.set_known_host",
  116. user=username,
  117. hostname="127.0.0.1",
  118. port=self.listen_port,
  119. enc="ssh-rsa",
  120. fingerprint="fd:6f:7f:5d:06:6b:f2:06:0d:26:93:9e:5a:b5:19:46",
  121. hash_known_hosts=False,
  122. fingerprint_hash_type="md5",
  123. )
  124. if ret.exitcode != 0:
  125. pytest.fail("Failed to run 'ssh.set_known_host'")
  126. if "error" in ret.json:
  127. pytest.fail("Failed to run 'ssh.set_known_host'")
  128. @attr.s(kw_only=True, slots=True)
  129. class UwsgiDaemon(DaemonFactory):
  130. config_dir = attr.ib()
  131. listen_port = attr.ib(default=attr.Factory(get_unused_localhost_port))
  132. def __attrs_post_init__(self):
  133. if self.check_ports is None:
  134. self.check_ports = []
  135. self.check_ports.append(self.listen_port)
  136. super().__attrs_post_init__()
  137. def get_base_script_args(self):
  138. """
  139. Returns any additional arguments to pass to the CLI script
  140. """
  141. return ["--yaml", os.path.join(self.config_dir, "uwsgi.yml")]
  142. def apply_pre_start_states(self, salt_call_cli, testclass, root_dir):
  143. if self.listen_port in self.check_ports:
  144. self.check_ports.remove(self.listen_port)
  145. if self.listen_port in self.listen_ports:
  146. self.listen_ports.remove(self.listen_port)
  147. self.listen_port = get_unused_localhost_port()
  148. self.check_ports.append(self.listen_port)
  149. self.listen_ports.append(self.listen_port)
  150. config_dir = os.path.join(root_dir, "config")
  151. git_dir = os.path.join(root_dir, "git")
  152. testclass.repo_dir = repo_dir = os.path.join(git_dir, "repos")
  153. venv_dir = os.path.join(root_dir, "venv")
  154. uwsgi_bin = os.path.join(venv_dir, "bin", "uwsgi")
  155. pillar = {
  156. "git_pillar": {
  157. "config_dir": config_dir,
  158. "git_dir": git_dir,
  159. "venv_dir": venv_dir,
  160. "root_dir": root_dir,
  161. "uwsgi_port": self.listen_port,
  162. }
  163. }
  164. # Different libexec dir for git backend on Debian and FreeBSD-based systems
  165. if salt.utils.platform.is_freebsd():
  166. git_core = "/usr/local/libexec/git-core"
  167. else:
  168. git_core = "/usr/libexec/git-core"
  169. if not os.path.exists(git_core):
  170. git_core = "/usr/lib/git-core"
  171. if not os.path.exists(git_core):
  172. pytest.fail(
  173. "{} not found. Either git is not installed, or the test "
  174. "class needs to be updated.".format(git_core)
  175. )
  176. pillar["git_pillar"]["git-http-backend"] = os.path.join(
  177. git_core, "git-http-backend"
  178. )
  179. ret = salt_call_cli.run(
  180. "state.apply", mods="git_pillar.http.uwsgi", pillar=pillar, _timeout=120
  181. )
  182. if ret.exitcode != 0:
  183. pytest.fail("Failed to apply the 'git_pillar.http.uwsgi' state")
  184. if next(iter(ret.json.values()))["result"] is not True:
  185. pytest.fail("Failed to apply the 'git_pillar.http.uwsgi' state")
  186. @attr.s(kw_only=True, slots=True)
  187. class NginxDaemon(DaemonFactory):
  188. config_dir = attr.ib()
  189. uwsgi_port = attr.ib()
  190. listen_port = attr.ib(default=attr.Factory(get_unused_localhost_port))
  191. def __attrs_post_init__(self):
  192. if self.check_ports is None:
  193. self.check_ports = []
  194. self.check_ports.append(self.listen_port)
  195. super().__attrs_post_init__()
  196. def get_base_script_args(self):
  197. """
  198. Returns any additional arguments to pass to the CLI script
  199. """
  200. return ["-c", os.path.join(self.config_dir, "nginx.conf")]
  201. def apply_pre_start_states(self, salt_call_cli, testclass, root_dir):
  202. if self.listen_port in self.check_ports:
  203. self.check_ports.remove(self.listen_port)
  204. if self.listen_port in self.listen_ports:
  205. self.listen_ports.remove(self.listen_port)
  206. self.listen_port = get_unused_localhost_port()
  207. self.check_ports.append(self.listen_port)
  208. self.listen_ports.append(self.listen_port)
  209. config_dir = os.path.join(root_dir, "config")
  210. git_dir = os.path.join(root_dir, "git")
  211. url = "http://127.0.0.1:{port}/repo.git".format(port=self.listen_port)
  212. url_extra_repo = "http://127.0.0.1:{port}/extra_repo.git".format(
  213. port=self.listen_port
  214. )
  215. ext_opts = {"url": url, "url_extra_repo": url_extra_repo}
  216. # Add auth params if present (if so this will trigger the spawned
  217. # server to turn on HTTP basic auth).
  218. for credential_param in ("user", "password"):
  219. if hasattr(testclass, credential_param):
  220. ext_opts[credential_param] = getattr(testclass, credential_param)
  221. testclass.ext_opts = ext_opts
  222. testclass.nginx_port = self.listen_port
  223. auth_enabled = hasattr(testclass, "username") and hasattr(testclass, "password")
  224. pillar = {
  225. "git_pillar": {
  226. "config_dir": config_dir,
  227. "git_dir": git_dir,
  228. "uwsgi_port": self.uwsgi_port,
  229. "nginx_port": self.listen_port,
  230. "auth_enabled": auth_enabled,
  231. }
  232. }
  233. ret = salt_call_cli.run(
  234. "state.apply", mods="git_pillar.http.nginx", pillar=pillar
  235. )
  236. if ret.exitcode != 0:
  237. pytest.fail("Failed to apply the 'git_pillar.http.nginx' state")
  238. if next(iter(ret.json.values()))["result"] is not True:
  239. pytest.fail("Failed to apply the 'git_pillar.http.nginx' state")
  240. @pytest.fixture(scope="class")
  241. def ssh_pillar_tests_prep(request, salt_master, salt_minion):
  242. """
  243. Stand up an SSHD server to serve up git repos for tests.
  244. """
  245. salt_call_cli = salt_minion.get_salt_call_cli()
  246. sshd_bin = salt.utils.path.which("sshd")
  247. sshd_config_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  248. sshd_proc = SshdDaemonFactory(
  249. cli_script_name=sshd_bin,
  250. config_dir=sshd_config_dir,
  251. start_timeout=120,
  252. display_name=request.cls.__name__,
  253. )
  254. sshd_proc.register_before_start_callback(
  255. sshd_proc.apply_pre_start_states,
  256. salt_call_cli=salt_call_cli,
  257. testclass=request.cls,
  258. username=salt_master.config["user"],
  259. )
  260. sshd_proc.register_after_start_callback(
  261. sshd_proc.set_known_host,
  262. salt_call_cli=salt_call_cli,
  263. username=salt_master.config["user"],
  264. )
  265. try:
  266. sshd_proc.start()
  267. yield
  268. finally:
  269. request.cls.ext_opts = None
  270. salt_call_cli.run(
  271. "state.single", "user.absent", name=request.cls.username, purge=True
  272. )
  273. shutil.rmtree(sshd_config_dir, ignore_errors=True)
  274. ssh_dir = os.path.expanduser("~/.ssh")
  275. for filename in (
  276. request.cls.id_rsa_nopass,
  277. request.cls.id_rsa_nopass + ".pub",
  278. request.cls.id_rsa_withpass,
  279. request.cls.id_rsa_withpass + ".pub",
  280. request.cls.git_ssh,
  281. ):
  282. try:
  283. os.remove(os.path.join(ssh_dir, filename))
  284. except OSError as exc:
  285. if exc.errno != errno.ENOENT:
  286. raise
  287. sshd_proc.terminate()
  288. @pytest.fixture(scope="class")
  289. def webserver_pillar_tests_prep(request, salt_master, salt_minion):
  290. """
  291. Stand up an nginx + uWSGI + git-http-backend webserver to
  292. serve up git repos for tests.
  293. """
  294. salt_call_cli = salt_minion.get_salt_call_cli()
  295. root_dir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  296. config_dir = os.path.join(root_dir, "config")
  297. venv_dir = os.path.join(root_dir, "venv")
  298. uwsgi_bin = os.path.join(venv_dir, "bin", "uwsgi")
  299. uwsgi_proc = nginx_proc = None
  300. try:
  301. uwsgi_proc = UwsgiDaemon(
  302. cli_script_name=uwsgi_bin,
  303. config_dir=config_dir,
  304. start_timeout=120,
  305. display_name=request.cls.__name__,
  306. )
  307. uwsgi_proc.register_before_start_callback(
  308. uwsgi_proc.apply_pre_start_states,
  309. salt_call_cli=salt_call_cli,
  310. testclass=request.cls,
  311. root_dir=root_dir,
  312. )
  313. uwsgi_proc.start()
  314. nginx_proc = NginxDaemon(
  315. cli_script_name="nginx",
  316. config_dir=config_dir,
  317. start_timeout=120,
  318. uwsgi_port=uwsgi_proc.listen_port,
  319. display_name=request.cls.__name__,
  320. )
  321. nginx_proc.register_before_start_callback(
  322. nginx_proc.apply_pre_start_states,
  323. salt_call_cli=salt_call_cli,
  324. testclass=request.cls,
  325. root_dir=root_dir,
  326. )
  327. nginx_proc.start()
  328. yield
  329. finally:
  330. request.cls.repo_dir = request.cls.ext_opts = request.cls.nginx_port = None
  331. if uwsgi_proc:
  332. uwsgi_proc.terminate()
  333. if nginx_proc:
  334. nginx_proc.terminate()
  335. shutil.rmtree(root_dir, ignore_errors=True)
  336. @pytest.fixture(scope="class")
  337. def webserver_pillar_tests_prep_authenticated(request, webserver_pillar_tests_prep):
  338. url = "http://{username}:{password}@127.0.0.1:{port}/repo.git".format(
  339. username=request.cls.username,
  340. password=request.cls.password,
  341. port=request.cls.nginx_port,
  342. )
  343. url_extra_repo = "http://{username}:{password}@127.0.0.1:{port}/extra_repo.git".format(
  344. username=request.cls.username,
  345. password=request.cls.password,
  346. port=request.cls.nginx_port,
  347. )
  348. request.cls.ext_opts["url"] = url
  349. request.cls.ext_opts["url_extra_repo"] = url_extra_repo
  350. request.cls.ext_opts["username"] = request.cls.username
  351. request.cls.ext_opts["password"] = request.cls.password
  352. @SKIP_IF_NOT_RUNNING_PYTEST
  353. class GitTestBase(ModuleCase):
  354. """
  355. Base class for all gitfs/git_pillar tests.
  356. """
  357. maxDiff = None
  358. git_opts = '-c user.name="Foo Bar" -c user.email=foo@bar.com'
  359. def make_repo(self, root_dir, user="root"):
  360. raise NotImplementedError()
  361. class GitFSTestBase(GitTestBase, LoaderModuleMockMixin):
  362. """
  363. Base class for all gitfs tests
  364. """
  365. @requires_system_grains
  366. def setup_loader_modules(self, grains): # pylint: disable=W0221
  367. return {gitfs: {"__opts__": _OPTS.copy(), "__grains__": grains}}
  368. def make_repo(self, root_dir, user="root"):
  369. raise NotImplementedError()
  370. class GitPillarTestBase(GitTestBase, LoaderModuleMockMixin):
  371. """
  372. Base class for all git_pillar tests
  373. """
  374. bare_repo = bare_repo_backup = bare_extra_repo = bare_extra_repo_backup = None
  375. admin_repo = admin_repo_backup = admin_extra_repo = admin_extra_repo_backup = None
  376. @requires_system_grains
  377. def setup_loader_modules(self, grains): # pylint: disable=W0221
  378. return {git_pillar: {"__opts__": _OPTS.copy(), "__grains__": grains}}
  379. def get_pillar(self, ext_pillar_conf):
  380. """
  381. Run git_pillar with the specified configuration
  382. """
  383. cachedir = tempfile.mkdtemp(dir=RUNTIME_VARS.TMP)
  384. self.addCleanup(shutil.rmtree, cachedir, ignore_errors=True)
  385. ext_pillar_opts = {"optimization_order": [0, 1, 2]}
  386. ext_pillar_opts.update(
  387. salt.utils.yaml.safe_load(
  388. ext_pillar_conf.format(
  389. cachedir=cachedir,
  390. extmods=os.path.join(cachedir, "extmods"),
  391. **self.ext_opts
  392. )
  393. )
  394. )
  395. with patch.dict(git_pillar.__opts__, ext_pillar_opts):
  396. return git_pillar.ext_pillar(
  397. "minion", {}, *ext_pillar_opts["ext_pillar"][0]["git"]
  398. )
  399. def make_repo(self, root_dir, user="root"):
  400. log.info("Creating test Git repo....")
  401. self.bare_repo = os.path.join(root_dir, "repo.git")
  402. self.bare_repo_backup = "{}.backup".format(self.bare_repo)
  403. self.admin_repo = os.path.join(root_dir, "admin")
  404. self.admin_repo_backup = "{}.backup".format(self.admin_repo)
  405. for dirname in (self.bare_repo, self.admin_repo):
  406. shutil.rmtree(dirname, ignore_errors=True)
  407. if os.path.exists(self.bare_repo_backup) and os.path.exists(
  408. self.admin_repo_backup
  409. ):
  410. shutil.copytree(self.bare_repo_backup, self.bare_repo)
  411. shutil.copytree(self.admin_repo_backup, self.admin_repo)
  412. return
  413. # Create bare repo
  414. self.run_function("git.init", [self.bare_repo], user=user, bare=True)
  415. # Clone bare repo
  416. self.run_function("git.clone", [self.admin_repo], url=self.bare_repo, user=user)
  417. def _push(branch, message):
  418. self.run_function("git.add", [self.admin_repo, "."], user=user)
  419. self.run_function(
  420. "git.commit",
  421. [self.admin_repo, message],
  422. user=user,
  423. git_opts=self.git_opts,
  424. )
  425. self.run_function(
  426. "git.push", [self.admin_repo], remote="origin", ref=branch, user=user,
  427. )
  428. with salt.utils.files.fopen(
  429. os.path.join(self.admin_repo, "top.sls"), "w"
  430. ) as fp_:
  431. fp_.write(
  432. textwrap.dedent(
  433. """\
  434. base:
  435. '*':
  436. - foo
  437. """
  438. )
  439. )
  440. with salt.utils.files.fopen(
  441. os.path.join(self.admin_repo, "foo.sls"), "w"
  442. ) as fp_:
  443. fp_.write(
  444. textwrap.dedent(
  445. """\
  446. branch: master
  447. mylist:
  448. - master
  449. mydict:
  450. master: True
  451. nested_list:
  452. - master
  453. nested_dict:
  454. master: True
  455. """
  456. )
  457. )
  458. # Add another file to be referenced using git_pillar_includes
  459. with salt.utils.files.fopen(
  460. os.path.join(self.admin_repo, "bar.sls"), "w"
  461. ) as fp_:
  462. fp_.write("included_pillar: True\n")
  463. # Add another file in subdir
  464. os.mkdir(os.path.join(self.admin_repo, "subdir"))
  465. with salt.utils.files.fopen(
  466. os.path.join(self.admin_repo, "subdir", "bar.sls"), "w"
  467. ) as fp_:
  468. fp_.write("from_subdir: True\n")
  469. _push("master", "initial commit")
  470. # Do the same with different values for "dev" branch
  471. self.run_function("git.checkout", [self.admin_repo], user=user, opts="-b dev")
  472. # The bar.sls shouldn't be in any branch but master
  473. self.run_function("git.rm", [self.admin_repo, "bar.sls"], user=user)
  474. with salt.utils.files.fopen(
  475. os.path.join(self.admin_repo, "top.sls"), "w"
  476. ) as fp_:
  477. fp_.write(
  478. textwrap.dedent(
  479. """\
  480. dev:
  481. '*':
  482. - foo
  483. """
  484. )
  485. )
  486. with salt.utils.files.fopen(
  487. os.path.join(self.admin_repo, "foo.sls"), "w"
  488. ) as fp_:
  489. fp_.write(
  490. textwrap.dedent(
  491. """\
  492. branch: dev
  493. mylist:
  494. - dev
  495. mydict:
  496. dev: True
  497. nested_list:
  498. - dev
  499. nested_dict:
  500. dev: True
  501. """
  502. )
  503. )
  504. _push("dev", "add dev branch")
  505. # Create just a top file in a separate repo, to be mapped to the base
  506. # env and referenced using git_pillar_includes
  507. self.run_function(
  508. "git.checkout", [self.admin_repo], user=user, opts="-b top_only"
  509. )
  510. # The top.sls should be the only file in this branch
  511. self.run_function(
  512. "git.rm",
  513. [self.admin_repo, "foo.sls", os.path.join("subdir", "bar.sls")],
  514. user=user,
  515. )
  516. with salt.utils.files.fopen(
  517. os.path.join(self.admin_repo, "top.sls"), "w"
  518. ) as fp_:
  519. fp_.write(
  520. textwrap.dedent(
  521. """\
  522. base:
  523. '*':
  524. - bar
  525. """
  526. )
  527. )
  528. _push("top_only", "add top_only branch")
  529. # Create just another top file in a separate repo, to be mapped to the base
  530. # env and including mounted.bar
  531. self.run_function(
  532. "git.checkout", [self.admin_repo], user=user, opts="-b top_mounted"
  533. )
  534. # The top.sls should be the only file in this branch
  535. with salt.utils.files.fopen(
  536. os.path.join(self.admin_repo, "top.sls"), "w"
  537. ) as fp_:
  538. fp_.write(
  539. textwrap.dedent(
  540. """\
  541. base:
  542. '*':
  543. - mounted.bar
  544. """
  545. )
  546. )
  547. _push("top_mounted", "add top_mounted branch")
  548. shutil.copytree(self.bare_repo, self.bare_repo_backup)
  549. shutil.copytree(self.admin_repo, self.admin_repo_backup)
  550. log.info("Test Git repo created.")
  551. def make_extra_repo(self, root_dir, user="root"):
  552. log.info("Creating extra test Git repo....")
  553. self.bare_extra_repo = os.path.join(root_dir, "extra_repo.git")
  554. self.bare_extra_repo_backup = "{}.backup".format(self.bare_extra_repo)
  555. self.admin_extra_repo = os.path.join(root_dir, "admin_extra")
  556. self.admin_extra_repo_backup = "{}.backup".format(self.admin_extra_repo)
  557. for dirname in (self.bare_extra_repo, self.admin_extra_repo):
  558. shutil.rmtree(dirname, ignore_errors=True)
  559. if os.path.exists(self.bare_extra_repo_backup) and os.path.exists(
  560. self.admin_extra_repo_backup
  561. ):
  562. shutil.copytree(self.bare_extra_repo_backup, self.bare_extra_repo)
  563. shutil.copytree(self.admin_extra_repo_backup, self.admin_extra_repo)
  564. return
  565. # Create bare extra repo
  566. self.run_function("git.init", [self.bare_extra_repo], user=user, bare=True)
  567. # Clone bare repo
  568. self.run_function(
  569. "git.clone", [self.admin_extra_repo], url=self.bare_extra_repo, user=user
  570. )
  571. def _push(branch, message):
  572. self.run_function("git.add", [self.admin_extra_repo, "."], user=user)
  573. self.run_function(
  574. "git.commit",
  575. [self.admin_extra_repo, message],
  576. user=user,
  577. git_opts=self.git_opts,
  578. )
  579. self.run_function(
  580. "git.push",
  581. [self.admin_extra_repo],
  582. remote="origin",
  583. ref=branch,
  584. user=user,
  585. )
  586. with salt.utils.files.fopen(
  587. os.path.join(self.admin_extra_repo, "top.sls"), "w"
  588. ) as fp_:
  589. fp_.write(
  590. textwrap.dedent(
  591. """\
  592. "{{saltenv}}":
  593. '*':
  594. - motd
  595. - nowhere.foo
  596. """
  597. )
  598. )
  599. with salt.utils.files.fopen(
  600. os.path.join(self.admin_extra_repo, "motd.sls"), "w"
  601. ) as fp_:
  602. fp_.write(
  603. textwrap.dedent(
  604. """\
  605. motd: The force will be with you. Always.
  606. """
  607. )
  608. )
  609. _push("master", "initial commit")
  610. shutil.copytree(self.bare_extra_repo, self.bare_extra_repo_backup)
  611. shutil.copytree(self.admin_extra_repo, self.admin_extra_repo_backup)
  612. log.info("Extra test Git repo created.")
  613. @classmethod
  614. def tearDownClass(cls):
  615. super().tearDownClass()
  616. for dirname in (
  617. cls.admin_repo,
  618. cls.admin_repo_backup,
  619. cls.admin_extra_repo,
  620. cls.admin_extra_repo_backup,
  621. cls.bare_repo,
  622. cls.bare_repo_backup,
  623. cls.bare_extra_repo,
  624. cls.bare_extra_repo_backup,
  625. ):
  626. if dirname is not None:
  627. shutil.rmtree(dirname, ignore_errors=True)
  628. class GitPillarSSHTestBase(GitPillarTestBase):
  629. """
  630. Base class for GitPython and Pygit2 SSH tests
  631. """
  632. id_rsa_nopass = id_rsa_withpass = None
  633. git_ssh = "/tmp/git_ssh"
  634. def setUp(self):
  635. """
  636. Create the SSH server and user, and create the git repo
  637. """
  638. log.info("%s.setUp() started...", self.__class__.__name__)
  639. super().setUp()
  640. root_dir = os.path.expanduser("~{}".format(self.username))
  641. if root_dir.startswith("~"):
  642. raise AssertionError(
  643. "Unable to resolve homedir for user '{}'".format(self.username)
  644. )
  645. self.make_repo(root_dir, user=self.username)
  646. self.make_extra_repo(root_dir, user=self.username)
  647. log.info("%s.setUp() complete.", self.__class__.__name__)
  648. def get_pillar(self, ext_pillar_conf):
  649. """
  650. Wrap the parent class' get_pillar() func in logic that temporarily
  651. changes the GIT_SSH to use our custom script, ensuring that the
  652. passphraselsess key is used to auth without needing to modify the root
  653. user's ssh config file.
  654. """
  655. with patched_environ(GIT_SSH=self.git_ssh):
  656. return super().get_pillar(ext_pillar_conf)
  657. class GitPillarHTTPTestBase(GitPillarTestBase):
  658. """
  659. Base class for GitPython and Pygit2 HTTP tests
  660. """
  661. def setUp(self):
  662. """
  663. Create and start the webserver, and create the git repo
  664. """
  665. log.info("%s.setUp() started...", self.__class__.__name__)
  666. super().setUp()
  667. self.make_repo(self.repo_dir)
  668. self.make_extra_repo(self.repo_dir)
  669. log.info("%s.setUp() complete", self.__class__.__name__)