test_postgresql.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. import pytest
  2. from flexmock import flexmock
  3. from borgmatic.hooks import postgresql as module
  4. def test_make_database_dump_filename_uses_name_and_hostname():
  5. flexmock(module.os.path).should_receive('expanduser').and_return('databases')
  6. assert module.make_database_dump_filename('test', 'hostname') == 'databases/hostname/test'
  7. def test_make_database_dump_filename_without_hostname_defaults_to_localhost():
  8. flexmock(module.os.path).should_receive('expanduser').and_return('databases')
  9. assert module.make_database_dump_filename('test') == 'databases/localhost/test'
  10. def test_make_database_dump_filename_with_invalid_name_raises():
  11. flexmock(module.os.path).should_receive('expanduser').and_return('databases')
  12. with pytest.raises(ValueError):
  13. module.make_database_dump_filename('invalid/name')
  14. def test_dump_databases_runs_pg_dump_for_each_database():
  15. databases = [{'name': 'foo'}, {'name': 'bar'}]
  16. flexmock(module).should_receive('make_database_dump_filename').and_return(
  17. 'databases/localhost/foo'
  18. ).and_return('databases/localhost/bar')
  19. flexmock(module.os).should_receive('makedirs')
  20. for name in ('foo', 'bar'):
  21. flexmock(module).should_receive('execute_command').with_args(
  22. (
  23. 'pg_dump',
  24. '--no-password',
  25. '--clean',
  26. '--file',
  27. 'databases/localhost/{}'.format(name),
  28. '--format',
  29. 'custom',
  30. name,
  31. ),
  32. extra_environment=None,
  33. ).once()
  34. module.dump_databases(databases, 'test.yaml', dry_run=False)
  35. def test_dump_databases_with_dry_run_skips_pg_dump():
  36. databases = [{'name': 'foo'}, {'name': 'bar'}]
  37. flexmock(module).should_receive('make_database_dump_filename').and_return(
  38. 'databases/localhost/foo'
  39. ).and_return('databases/localhost/bar')
  40. flexmock(module.os).should_receive('makedirs')
  41. flexmock(module).should_receive('execute_command').never()
  42. module.dump_databases(databases, 'test.yaml', dry_run=True)
  43. def test_dump_databases_without_databases_does_not_raise():
  44. module.dump_databases([], 'test.yaml', dry_run=False)
  45. def test_dump_databases_with_invalid_database_name_raises():
  46. databases = [{'name': 'heehee/../../etc/passwd'}]
  47. with pytest.raises(ValueError):
  48. module.dump_databases(databases, 'test.yaml', dry_run=True)
  49. def test_dump_databases_runs_pg_dump_with_hostname_and_port():
  50. databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
  51. flexmock(module).should_receive('make_database_dump_filename').and_return(
  52. 'databases/database.example.org/foo'
  53. )
  54. flexmock(module.os).should_receive('makedirs')
  55. flexmock(module).should_receive('execute_command').with_args(
  56. (
  57. 'pg_dump',
  58. '--no-password',
  59. '--clean',
  60. '--file',
  61. 'databases/database.example.org/foo',
  62. '--host',
  63. 'database.example.org',
  64. '--port',
  65. '5433',
  66. '--format',
  67. 'custom',
  68. 'foo',
  69. ),
  70. extra_environment=None,
  71. ).once()
  72. module.dump_databases(databases, 'test.yaml', dry_run=False)
  73. def test_dump_databases_runs_pg_dump_with_username_and_password():
  74. databases = [{'name': 'foo', 'username': 'postgres', 'password': 'trustsome1'}]
  75. flexmock(module).should_receive('make_database_dump_filename').and_return(
  76. 'databases/localhost/foo'
  77. )
  78. flexmock(module.os).should_receive('makedirs')
  79. flexmock(module).should_receive('execute_command').with_args(
  80. (
  81. 'pg_dump',
  82. '--no-password',
  83. '--clean',
  84. '--file',
  85. 'databases/localhost/foo',
  86. '--username',
  87. 'postgres',
  88. '--format',
  89. 'custom',
  90. 'foo',
  91. ),
  92. extra_environment={'PGPASSWORD': 'trustsome1'},
  93. ).once()
  94. module.dump_databases(databases, 'test.yaml', dry_run=False)
  95. def test_dump_databases_runs_pg_dump_with_format():
  96. databases = [{'name': 'foo', 'format': 'tar'}]
  97. flexmock(module).should_receive('make_database_dump_filename').and_return(
  98. 'databases/localhost/foo'
  99. )
  100. flexmock(module.os).should_receive('makedirs')
  101. flexmock(module).should_receive('execute_command').with_args(
  102. (
  103. 'pg_dump',
  104. '--no-password',
  105. '--clean',
  106. '--file',
  107. 'databases/localhost/foo',
  108. '--format',
  109. 'tar',
  110. 'foo',
  111. ),
  112. extra_environment=None,
  113. ).once()
  114. module.dump_databases(databases, 'test.yaml', dry_run=False)
  115. def test_dump_databases_runs_pg_dump_with_options():
  116. databases = [{'name': 'foo', 'options': '--stuff=such'}]
  117. flexmock(module).should_receive('make_database_dump_filename').and_return(
  118. 'databases/localhost/foo'
  119. )
  120. flexmock(module.os).should_receive('makedirs')
  121. flexmock(module).should_receive('execute_command').with_args(
  122. (
  123. 'pg_dump',
  124. '--no-password',
  125. '--clean',
  126. '--file',
  127. 'databases/localhost/foo',
  128. '--format',
  129. 'custom',
  130. '--stuff=such',
  131. 'foo',
  132. ),
  133. extra_environment=None,
  134. ).once()
  135. module.dump_databases(databases, 'test.yaml', dry_run=False)
  136. def test_dump_databases_runs_pg_dumpall_for_all_databases():
  137. databases = [{'name': 'all'}]
  138. flexmock(module).should_receive('make_database_dump_filename').and_return(
  139. 'databases/localhost/all'
  140. )
  141. flexmock(module.os).should_receive('makedirs')
  142. flexmock(module).should_receive('execute_command').with_args(
  143. ('pg_dumpall', '--no-password', '--clean', '--file', 'databases/localhost/all'),
  144. extra_environment=None,
  145. ).once()
  146. module.dump_databases(databases, 'test.yaml', dry_run=False)
  147. def test_remove_database_dumps_removes_dump_for_each_database():
  148. databases = [{'name': 'foo'}, {'name': 'bar'}]
  149. flexmock(module).should_receive('make_database_dump_filename').and_return(
  150. 'databases/localhost/foo'
  151. ).and_return('databases/localhost/bar')
  152. flexmock(module.os).should_receive('listdir').and_return([])
  153. flexmock(module.os).should_receive('rmdir')
  154. for name in ('foo', 'bar'):
  155. flexmock(module.os).should_receive('remove').with_args(
  156. 'databases/localhost/{}'.format(name)
  157. ).once()
  158. module.remove_database_dumps(databases, 'test.yaml', dry_run=False)
  159. def test_remove_database_dumps_with_dry_run_skips_removal():
  160. databases = [{'name': 'foo'}, {'name': 'bar'}]
  161. flexmock(module.os).should_receive('remove').never()
  162. module.remove_database_dumps(databases, 'test.yaml', dry_run=True)
  163. def test_remove_database_dumps_without_databases_does_not_raise():
  164. module.remove_database_dumps([], 'test.yaml', dry_run=False)
  165. def test_remove_database_dumps_with_invalid_database_name_raises():
  166. databases = [{'name': 'heehee/../../etc/passwd'}]
  167. with pytest.raises(ValueError):
  168. module.remove_database_dumps(databases, 'test.yaml', dry_run=True)
  169. def test_make_database_dump_patterns_converts_names_to_glob_paths():
  170. flexmock(module).should_receive('make_database_dump_filename').and_return(
  171. 'databases/*/foo'
  172. ).and_return('databases/*/bar')
  173. assert module.make_database_dump_patterns(('foo', 'bar')) == [
  174. 'databases/*/foo',
  175. 'databases/*/bar',
  176. ]
  177. def test_make_database_dump_patterns_treats_empty_names_as_matching_all_databases():
  178. flexmock(module).should_receive('make_database_dump_filename').with_args('*', '*').and_return(
  179. 'databases/*/*'
  180. )
  181. assert module.make_database_dump_patterns(()) == ['databases/*/*']
  182. def test_convert_glob_patterns_to_borg_patterns_removes_leading_slash():
  183. assert module.convert_glob_patterns_to_borg_patterns(('/etc/foo/bar',)) == ['sh:etc/foo/bar']
  184. def test_get_database_names_from_dumps_gets_names_from_filenames_matching_globs():
  185. flexmock(module.glob).should_receive('glob').and_return(
  186. ('databases/localhost/foo',)
  187. ).and_return(('databases/localhost/bar',)).and_return(())
  188. assert module.get_database_names_from_dumps(
  189. ('databases/*/foo', 'databases/*/bar', 'databases/*/baz')
  190. ) == ['foo', 'bar']