test_database.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777
  1. import json
  2. import os
  3. import shutil
  4. import subprocess
  5. import sys
  6. import tempfile
  7. import pymongo
  8. import pytest
  9. import ruamel.yaml
  10. from borgmatic.hooks.data_source import config
  11. def write_configuration(
  12. source_directory,
  13. config_path,
  14. repository_path,
  15. user_runtime_directory,
  16. postgresql_dump_format='custom',
  17. postgresql_all_dump_format=None,
  18. mariadb_mysql_all_dump_format=None,
  19. mongodb_dump_format='archive',
  20. ):
  21. '''
  22. Write out borgmatic configuration into a file at the config path. Set the options so as to work
  23. for testing. This includes injecting the given repository path, borgmatic source directory for
  24. storing database dumps, dump format (for PostgreSQL), and encryption passphrase.
  25. '''
  26. postgresql_all_format_option = (
  27. f'format: {postgresql_all_dump_format}' if postgresql_all_dump_format else ''
  28. )
  29. mariadb_mysql_dump_format_option = (
  30. f'format: {mariadb_mysql_all_dump_format}' if mariadb_mysql_all_dump_format else ''
  31. )
  32. config_yaml = f'''
  33. source_directories:
  34. - {source_directory}
  35. repositories:
  36. - path: {repository_path}
  37. user_runtime_directory: {user_runtime_directory}
  38. encryption_passphrase: "test"
  39. postgresql_databases:
  40. - name: test
  41. hostname: postgresql
  42. username: postgres
  43. password: test
  44. format: {postgresql_dump_format}
  45. - name: all
  46. {postgresql_all_format_option}
  47. hostname: postgresql
  48. username: postgres
  49. password: test
  50. mariadb_databases:
  51. - name: test
  52. hostname: mariadb
  53. username: root
  54. password: test
  55. - name: all
  56. {mariadb_mysql_dump_format_option}
  57. hostname: mariadb
  58. username: root
  59. password: test
  60. mysql_databases:
  61. - name: test
  62. hostname: not-actually-mysql
  63. username: root
  64. password: test
  65. - name: all
  66. {mariadb_mysql_dump_format_option}
  67. hostname: not-actually-mysql
  68. username: root
  69. password: test
  70. mongodb_databases:
  71. - name: test
  72. hostname: mongodb
  73. username: root
  74. password: test
  75. authentication_database: admin
  76. format: {mongodb_dump_format}
  77. - name: all
  78. hostname: mongodb
  79. username: root
  80. password: test
  81. sqlite_databases:
  82. - name: sqlite_test
  83. path: /tmp/sqlite_test.db
  84. '''
  85. with open(config_path, 'w') as config_file:
  86. config_file.write(config_yaml)
  87. return ruamel.yaml.YAML(typ='safe').load(config_yaml)
  88. def write_container_configuration(
  89. source_directory,
  90. config_path,
  91. repository_path,
  92. user_runtime_directory,
  93. ):
  94. '''
  95. Write out borgmatic configuration into a file at the config path. Set the options so as to work
  96. for testing. This includes injecting the given repository path, borgmatic source directory for
  97. storing database dumps, and encryption passphrase.
  98. '''
  99. config_yaml = f'''
  100. source_directories:
  101. - {source_directory}
  102. repositories:
  103. - path: {repository_path}
  104. user_runtime_directory: {user_runtime_directory}
  105. encryption_passphrase: "test"
  106. postgresql_databases:
  107. - name: test
  108. hostname: postgresql
  109. username: postgres
  110. password: test
  111. restore_hostname: postgresql2
  112. restore_port: 5433
  113. restore_password: test2
  114. mariadb_databases:
  115. - name: test
  116. hostname: mariadb
  117. username: root
  118. password: test
  119. restore_hostname: mariadb2
  120. restore_port: 3307
  121. restore_username: root
  122. restore_password: test2
  123. mysql_databases:
  124. - name: test
  125. hostname: not-actually-mysql
  126. username: root
  127. password: test
  128. mongodb_databases:
  129. - name: test
  130. hostname: mongodb
  131. username: root
  132. password: test
  133. authentication_database: admin
  134. sqlite_databases:
  135. - name: sqlite_test
  136. path: /tmp/sqlite_test.db
  137. '''
  138. with open(config_path, 'w') as config_file:
  139. config_file.write(config_yaml)
  140. return ruamel.yaml.YAML(typ='safe').load(config_yaml)
  141. @pytest.mark.parametrize(
  142. 'postgresql_all_dump_format,mariadb_mysql_all_dump_format',
  143. (
  144. (None, None),
  145. ('custom', 'sql'),
  146. ),
  147. )
  148. def write_custom_restore_configuration(
  149. source_directory,
  150. config_path,
  151. repository_path,
  152. user_runtime_directory,
  153. postgresql_dump_format='custom',
  154. postgresql_all_dump_format=None,
  155. mariadb_mysql_all_dump_format=None,
  156. mongodb_dump_format='archive',
  157. ):
  158. '''
  159. Write out borgmatic configuration into a file at the config path. Set the options so as to work
  160. for testing with custom restore options. This includes a custom restore_hostname, restore_port,
  161. restore_username, restore_password and restore_path.
  162. '''
  163. config_yaml = f'''
  164. source_directories:
  165. - {source_directory}
  166. repositories:
  167. - path: {repository_path}
  168. user_runtime_directory: {user_runtime_directory}
  169. encryption_passphrase: "test"
  170. postgresql_databases:
  171. - name: test
  172. hostname: postgresql
  173. username: postgres
  174. password: test
  175. format: {postgresql_dump_format}
  176. restore_hostname: postgresql2
  177. restore_port: 5433
  178. restore_password: test2
  179. mariadb_databases:
  180. - name: test
  181. hostname: mariadb
  182. username: root
  183. password: test
  184. restore_hostname: mariadb2
  185. restore_port: 3307
  186. restore_username: root
  187. restore_password: test2
  188. mysql_databases:
  189. - name: test
  190. hostname: not-actually-mysql
  191. username: root
  192. password: test
  193. restore_hostname: not-actually-mysql2
  194. restore_port: 3307
  195. restore_username: root
  196. restore_password: test2
  197. mongodb_databases:
  198. - name: test
  199. hostname: mongodb
  200. username: root
  201. password: test
  202. authentication_database: admin
  203. format: {mongodb_dump_format}
  204. restore_hostname: mongodb2
  205. restore_port: 27018
  206. restore_username: root2
  207. restore_password: test2
  208. sqlite_databases:
  209. - name: sqlite_test
  210. path: /tmp/sqlite_test.db
  211. restore_path: /tmp/sqlite_test2.db
  212. '''
  213. with open(config_path, 'w') as config_file:
  214. config_file.write(config_yaml)
  215. return ruamel.yaml.YAML(typ='safe').load(config_yaml)
  216. def write_simple_custom_restore_configuration(
  217. source_directory,
  218. config_path,
  219. repository_path,
  220. user_runtime_directory,
  221. postgresql_dump_format='custom',
  222. ):
  223. '''
  224. Write out borgmatic configuration into a file at the config path. Set the options so as to work
  225. for testing with custom restore options, but this time using CLI arguments. This includes a
  226. custom restore_hostname, restore_port, restore_username and restore_password as we only test
  227. these options for PostgreSQL.
  228. '''
  229. config_yaml = f'''
  230. source_directories:
  231. - {source_directory}
  232. repositories:
  233. - path: {repository_path}
  234. user_runtime_directory: {user_runtime_directory}
  235. encryption_passphrase: "test"
  236. postgresql_databases:
  237. - name: test
  238. hostname: postgresql
  239. username: postgres
  240. password: test
  241. format: {postgresql_dump_format}
  242. '''
  243. with open(config_path, 'w') as config_file:
  244. config_file.write(config_yaml)
  245. return ruamel.yaml.YAML(typ='safe').load(config_yaml)
  246. def get_connection_params(database, use_restore_options=False):
  247. hostname = config.resolve_database_option('hostname', database, restore=use_restore_options)
  248. port = config.resolve_database_option('port', database, restore=use_restore_options)
  249. username = config.resolve_database_option('username', database, restore=use_restore_options)
  250. password = config.resolve_database_option('password', database, restore=use_restore_options)
  251. return (hostname, port, username, password)
  252. def run_postgresql_command(command, config, use_restore_options=False):
  253. (hostname, port, username, password) = get_connection_params(
  254. config['postgresql_databases'][0],
  255. use_restore_options,
  256. )
  257. subprocess.check_call(
  258. [
  259. '/usr/bin/psql',
  260. f'--host={hostname}',
  261. f'--port={port or 5432}',
  262. f"--username={username or 'root'}",
  263. f'--command={command}',
  264. 'test',
  265. ],
  266. env={'PGPASSWORD': password},
  267. )
  268. def run_mariadb_command(command, config, use_restore_options=False, binary_name='mariadb'):
  269. (hostname, port, username, password) = get_connection_params(
  270. config[f'{binary_name}_databases'][0],
  271. use_restore_options,
  272. )
  273. subprocess.check_call(
  274. [
  275. f'/usr/bin/{binary_name}',
  276. f'--host={hostname}',
  277. f'--port={port or 3306}',
  278. f'--user={username}',
  279. f'--execute={command}',
  280. 'test',
  281. ],
  282. env={'MYSQL_PWD': password},
  283. )
  284. def get_mongodb_database_client(config, use_restore_options=False):
  285. (hostname, port, username, password) = get_connection_params(
  286. config['mongodb_databases'][0],
  287. use_restore_options,
  288. )
  289. return pymongo.MongoClient(f'mongodb://{username}:{password}@{hostname}:{port or 27017}').test
  290. def run_sqlite_command(command, config, use_restore_options=False):
  291. database = config['sqlite_databases'][0]
  292. path = (database.get('restore_path') if use_restore_options else None) or database.get('path')
  293. subprocess.check_call(
  294. [
  295. '/usr/bin/sqlite3',
  296. path,
  297. command,
  298. '.exit',
  299. ],
  300. )
  301. DEFAULT_HOOK_NAMES = {'postgresql', 'mariadb', 'mysql', 'mongodb', 'sqlite'}
  302. def create_test_tables(config, use_restore_options=False):
  303. '''
  304. Create test tables for borgmatic to dump and backup.
  305. '''
  306. command = 'create table test{id} (thing int); insert into test{id} values (1);'
  307. if 'postgresql_databases' in config:
  308. run_postgresql_command(command.format(id=1), config, use_restore_options)
  309. if 'mariadb_databases' in config:
  310. run_mariadb_command(command.format(id=2), config, use_restore_options)
  311. if 'mysql_databases' in config:
  312. run_mariadb_command(command.format(id=3), config, use_restore_options, binary_name='mysql')
  313. if 'mongodb_databases' in config:
  314. get_mongodb_database_client(config, use_restore_options)['test4'].insert_one({'thing': 1})
  315. if 'sqlite_databases' in config:
  316. run_sqlite_command(command.format(id=5), config, use_restore_options)
  317. def drop_test_tables(config, use_restore_options=False):
  318. '''
  319. Drop the test tables in preparation for borgmatic restoring them.
  320. '''
  321. command = 'drop table if exists test{id};'
  322. if 'postgresql_databases' in config:
  323. run_postgresql_command(command.format(id=1), config, use_restore_options)
  324. if 'mariadb_databases' in config:
  325. run_mariadb_command(command.format(id=2), config, use_restore_options)
  326. if 'mysql_databases' in config:
  327. run_mariadb_command(command.format(id=3), config, use_restore_options, binary_name='mysql')
  328. if 'mongodb_databases' in config:
  329. get_mongodb_database_client(config, use_restore_options)['test4'].drop()
  330. if 'sqlite_databases' in config:
  331. run_sqlite_command(command.format(id=5), config, use_restore_options)
  332. def select_test_tables(config, use_restore_options=False):
  333. '''
  334. Select the test tables to make sure they exist.
  335. Raise if the expected tables cannot be selected, for instance if a restore hasn't worked as
  336. expected.
  337. '''
  338. command = 'select count(*) from test{id};'
  339. if 'postgresql_databases' in config:
  340. run_postgresql_command(command.format(id=1), config, use_restore_options)
  341. if 'mariadb_databases' in config:
  342. run_mariadb_command(command.format(id=2), config, use_restore_options)
  343. if 'mysql_databases' in config:
  344. run_mariadb_command(command.format(id=3), config, use_restore_options, binary_name='mysql')
  345. if 'mongodb_databases' in config:
  346. assert (
  347. get_mongodb_database_client(config, use_restore_options)['test4'].count_documents(
  348. filter={},
  349. )
  350. > 0
  351. )
  352. if 'sqlite_databases' in config:
  353. run_sqlite_command(command.format(id=5), config, use_restore_options)
  354. def test_database_dump_and_restore():
  355. # Create a Borg repository.
  356. temporary_directory = tempfile.mkdtemp()
  357. repository_path = os.path.join(temporary_directory, 'test.borg')
  358. # Write out a special file to ensure that it gets properly excluded and Borg doesn't hang on it.
  359. os.mkfifo(os.path.join(temporary_directory, 'special_file'))
  360. original_working_directory = os.getcwd()
  361. try:
  362. config_path = os.path.join(temporary_directory, 'test.yaml')
  363. config = write_configuration(
  364. temporary_directory,
  365. config_path,
  366. repository_path,
  367. temporary_directory,
  368. )
  369. create_test_tables(config)
  370. select_test_tables(config)
  371. subprocess.check_call(
  372. [
  373. 'borgmatic',
  374. '-v',
  375. '2',
  376. '--config',
  377. config_path,
  378. 'repo-create',
  379. '--encryption',
  380. 'repokey',
  381. ],
  382. )
  383. # Run borgmatic to generate a backup archive including database dumps.
  384. subprocess.check_call(['borgmatic', 'create', '--config', config_path, '-v', '2'])
  385. # Get the created archive name.
  386. output = subprocess.check_output(
  387. ['borgmatic', '--config', config_path, 'list', '--json'],
  388. ).decode(sys.stdout.encoding)
  389. parsed_output = json.loads(output)
  390. assert len(parsed_output) == 1
  391. assert len(parsed_output[0]['archives']) == 1
  392. archive_name = parsed_output[0]['archives'][0]['archive']
  393. # Restore the databases from the archive.
  394. drop_test_tables(config)
  395. subprocess.check_call(
  396. ['borgmatic', '-v', '2', '--config', config_path, 'restore', '--archive', archive_name],
  397. )
  398. # Ensure the test tables have actually been restored.
  399. select_test_tables(config)
  400. finally:
  401. os.chdir(original_working_directory)
  402. shutil.rmtree(temporary_directory)
  403. drop_test_tables(config)
  404. def test_database_dump_and_restore_with_restore_cli_flags():
  405. # Create a Borg repository.
  406. temporary_directory = tempfile.mkdtemp()
  407. repository_path = os.path.join(temporary_directory, 'test.borg')
  408. original_working_directory = os.getcwd()
  409. try:
  410. config_path = os.path.join(temporary_directory, 'test.yaml')
  411. config = write_simple_custom_restore_configuration(
  412. temporary_directory,
  413. config_path,
  414. repository_path,
  415. temporary_directory,
  416. )
  417. create_test_tables(config)
  418. select_test_tables(config)
  419. subprocess.check_call(
  420. [
  421. 'borgmatic',
  422. '-v',
  423. '2',
  424. '--config',
  425. config_path,
  426. 'repo-create',
  427. '--encryption',
  428. 'repokey',
  429. ],
  430. )
  431. # Run borgmatic to generate a backup archive including a database dump.
  432. subprocess.check_call(['borgmatic', 'create', '--config', config_path, '-v', '2'])
  433. # Get the created archive name.
  434. output = subprocess.check_output(
  435. ['borgmatic', '--config', config_path, 'list', '--json'],
  436. ).decode(sys.stdout.encoding)
  437. parsed_output = json.loads(output)
  438. assert len(parsed_output) == 1
  439. assert len(parsed_output[0]['archives']) == 1
  440. archive_name = parsed_output[0]['archives'][0]['archive']
  441. # Restore the database from the archive.
  442. drop_test_tables(config)
  443. subprocess.check_call(
  444. [
  445. 'borgmatic',
  446. '-v',
  447. '2',
  448. '--config',
  449. config_path,
  450. 'restore',
  451. '--archive',
  452. archive_name,
  453. '--hostname',
  454. 'postgresql2',
  455. '--port',
  456. '5433',
  457. '--password',
  458. 'test2',
  459. ],
  460. )
  461. # Ensure the test tables have actually been restored. But first modify the config to contain
  462. # the altered restore values from the borgmatic command above. This ensures that the test
  463. # tables are selected from the correct database.
  464. database = config['postgresql_databases'][0]
  465. database['restore_hostname'] = 'postgresql2'
  466. database['restore_port'] = '5433'
  467. database['restore_password'] = 'test2'
  468. select_test_tables(config, use_restore_options=True)
  469. finally:
  470. os.chdir(original_working_directory)
  471. shutil.rmtree(temporary_directory)
  472. drop_test_tables(config)
  473. drop_test_tables(config, use_restore_options=True)
  474. def test_database_dump_and_restore_with_restore_configuration_options():
  475. # Create a Borg repository.
  476. temporary_directory = tempfile.mkdtemp()
  477. repository_path = os.path.join(temporary_directory, 'test.borg')
  478. original_working_directory = os.getcwd()
  479. try:
  480. config_path = os.path.join(temporary_directory, 'test.yaml')
  481. config = write_custom_restore_configuration(
  482. temporary_directory,
  483. config_path,
  484. repository_path,
  485. temporary_directory,
  486. )
  487. create_test_tables(config)
  488. select_test_tables(config)
  489. subprocess.check_call(
  490. [
  491. 'borgmatic',
  492. '-v',
  493. '2',
  494. '--config',
  495. config_path,
  496. 'repo-create',
  497. '--encryption',
  498. 'repokey',
  499. ],
  500. )
  501. # Run borgmatic to generate a backup archive including a database dump.
  502. subprocess.check_call(['borgmatic', 'create', '--config', config_path, '-v', '2'])
  503. # Get the created archive name.
  504. output = subprocess.check_output(
  505. ['borgmatic', '--config', config_path, 'list', '--json'],
  506. ).decode(sys.stdout.encoding)
  507. parsed_output = json.loads(output)
  508. assert len(parsed_output) == 1
  509. assert len(parsed_output[0]['archives']) == 1
  510. archive_name = parsed_output[0]['archives'][0]['archive']
  511. # Restore the database from the archive.
  512. drop_test_tables(config)
  513. subprocess.check_call(
  514. ['borgmatic', '-v', '2', '--config', config_path, 'restore', '--archive', archive_name],
  515. )
  516. # Ensure the test tables have actually been restored.
  517. select_test_tables(config, use_restore_options=True)
  518. finally:
  519. os.chdir(original_working_directory)
  520. shutil.rmtree(temporary_directory)
  521. drop_test_tables(config)
  522. drop_test_tables(config, use_restore_options=True)
  523. def test_database_dump_and_restore_with_directory_format():
  524. # Create a Borg repository.
  525. temporary_directory = tempfile.mkdtemp()
  526. repository_path = os.path.join(temporary_directory, 'test.borg')
  527. original_working_directory = os.getcwd()
  528. try:
  529. config_path = os.path.join(temporary_directory, 'test.yaml')
  530. config = write_configuration(
  531. temporary_directory,
  532. config_path,
  533. repository_path,
  534. temporary_directory,
  535. postgresql_dump_format='directory',
  536. mongodb_dump_format='directory',
  537. )
  538. create_test_tables(config)
  539. select_test_tables(config)
  540. subprocess.check_call(
  541. [
  542. 'borgmatic',
  543. '-v',
  544. '2',
  545. '--config',
  546. config_path,
  547. 'repo-create',
  548. '--encryption',
  549. 'repokey',
  550. ],
  551. )
  552. # Run borgmatic to generate a backup archive including a database dump.
  553. subprocess.check_call(['borgmatic', 'create', '--config', config_path, '-v', '2'])
  554. # Restore the database from the archive.
  555. drop_test_tables(config)
  556. subprocess.check_call(
  557. ['borgmatic', '--config', config_path, 'restore', '--archive', 'latest'],
  558. )
  559. # Ensure the test tables have actually been restored.
  560. select_test_tables(config)
  561. finally:
  562. os.chdir(original_working_directory)
  563. shutil.rmtree(temporary_directory)
  564. drop_test_tables(config)
  565. def test_database_dump_with_error_causes_borgmatic_to_exit():
  566. # Create a Borg repository.
  567. temporary_directory = tempfile.mkdtemp()
  568. repository_path = os.path.join(temporary_directory, 'test.borg')
  569. original_working_directory = os.getcwd()
  570. try:
  571. config_path = os.path.join(temporary_directory, 'test.yaml')
  572. write_configuration(temporary_directory, config_path, repository_path, temporary_directory)
  573. subprocess.check_call(
  574. [
  575. 'borgmatic',
  576. '-v',
  577. '2',
  578. '--config',
  579. config_path,
  580. 'repo-create',
  581. '--encryption',
  582. 'repokey',
  583. ],
  584. )
  585. # Run borgmatic with a config override such that the database dump fails.
  586. with pytest.raises(subprocess.CalledProcessError):
  587. subprocess.check_call(
  588. [
  589. 'borgmatic',
  590. 'create',
  591. '--config',
  592. config_path,
  593. '-v',
  594. '2',
  595. '--override',
  596. "hooks.postgresql_databases=[{'name': 'nope'}]",
  597. ],
  598. )
  599. finally:
  600. os.chdir(original_working_directory)
  601. shutil.rmtree(temporary_directory)
  602. def test_database_dump_and_restore_containers():
  603. # Create a Borg repository.
  604. temporary_directory = tempfile.mkdtemp()
  605. repository_path = os.path.join(temporary_directory, 'test.borg')
  606. original_working_directory = os.getcwd()
  607. original_path = os.environ.get('PATH', '')
  608. os.environ['PATH'] = f'/app/tests/end-to-end/commands:{original_path}'
  609. try:
  610. config_path = os.path.join(temporary_directory, 'test.yaml')
  611. config = write_container_configuration(
  612. temporary_directory,
  613. config_path,
  614. repository_path,
  615. temporary_directory,
  616. )
  617. create_test_tables(config)
  618. select_test_tables(config)
  619. subprocess.check_call(
  620. [
  621. 'borgmatic',
  622. '-v',
  623. '2',
  624. '--config',
  625. config_path,
  626. 'repo-create',
  627. '--encryption',
  628. 'repokey',
  629. ],
  630. )
  631. # Run borgmatic to generate a backup archive including database dumps.
  632. subprocess.check_call(['borgmatic', 'create', '--config', config_path, '-v', '2'])
  633. # Get the created archive name.
  634. output = subprocess.check_output(
  635. ['borgmatic', '--config', config_path, 'list', '--json'],
  636. ).decode(sys.stdout.encoding)
  637. parsed_output = json.loads(output)
  638. assert len(parsed_output) == 1
  639. assert len(parsed_output[0]['archives']) == 1
  640. archive_name = parsed_output[0]['archives'][0]['archive']
  641. # Restore the databases from the archive.
  642. drop_test_tables(config)
  643. subprocess.check_call(
  644. ['borgmatic', '-v', '2', '--config', config_path, 'restore', '--archive', archive_name],
  645. )
  646. # Ensure the test tables have actually been restored.
  647. select_test_tables(config, use_restore_options=True)
  648. finally:
  649. os.chdir(original_working_directory)
  650. shutil.rmtree(temporary_directory)
  651. drop_test_tables(config)
  652. os.environ['PATH'] = original_path