test_monitoring.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. import http.server
  2. import json
  3. import os
  4. import shutil
  5. import subprocess
  6. import sys
  7. import tempfile
  8. import threading
  9. import pytest
  10. def generate_configuration(config_path, repository_path, monitoring_hook_configuration):
  11. '''
  12. Generate borgmatic configuration into a file at the config path, and update the defaults so as
  13. to work for testing, including updating the source directories, injecting the given repository
  14. path, and tacking on an encryption passphrase.
  15. '''
  16. subprocess.check_call(f'borgmatic config generate --destination {config_path}'.split(' '))
  17. config = (
  18. open(config_path)
  19. .read()
  20. .replace('ssh://user@backupserver/./sourcehostname.borg', repository_path)
  21. .replace('- path: /mnt/backup', '')
  22. .replace('label: local', '')
  23. .replace('- /home/user/path with spaces', '')
  24. .replace('- /home', f'- {config_path}')
  25. .replace('- /etc', '')
  26. .replace('- /var/log/syslog*', '')
  27. + '\nencryption_passphrase: "test"'
  28. + f'\n{monitoring_hook_configuration}'
  29. )
  30. config_file = open(config_path, 'w')
  31. config_file.write(config)
  32. config_file.close()
  33. class Web_server(http.server.BaseHTTPRequestHandler):
  34. def handle_method(self):
  35. self.send_response(http.HTTPStatus.OK)
  36. self.send_header('Content-type', 'text/html')
  37. self.end_headers()
  38. self.wfile.write(''.encode('utf-8'))
  39. def do_GET(self):
  40. self.handle_method()
  41. def do_POST(self):
  42. self.handle_method()
  43. def serve_web_request(count):
  44. for index in range(0, count):
  45. with http.server.HTTPServer(('localhost', 12345), Web_server) as server:
  46. server.handle_request()
  47. class Background_web_server:
  48. def __init__(self, expected_request_count):
  49. self.expected_request_count = expected_request_count
  50. def __enter__(self):
  51. self.thread = threading.Thread(
  52. target=lambda: serve_web_request(count=self.expected_request_count)
  53. )
  54. self.thread.start()
  55. def __exit__(self, exception, value, traceback):
  56. self.thread.join()
  57. START_AND_FINISH = 2
  58. START_LOG_AND_FINISH = 3
  59. @pytest.mark.parametrize(
  60. 'monitoring_hook_configuration,expected_request_count',
  61. (
  62. (
  63. 'cronhub:\n ping_url: http://localhost:12345/start/1f5e3410-254c-11e8-b61d-55875966d031',
  64. START_AND_FINISH,
  65. ),
  66. (
  67. 'cronitor:\n ping_url: http://localhost:12345/d3x0c1',
  68. START_AND_FINISH,
  69. ),
  70. (
  71. 'healthchecks:\n ping_url: http://localhost:12345/addffa72-da17-40ae-be9c-ff591afb942a',
  72. START_LOG_AND_FINISH,
  73. ),
  74. (
  75. 'loki:\n url: http://localhost:12345/loki/api/v1/push\n labels:\n app: borgmatic',
  76. START_AND_FINISH,
  77. ),
  78. (
  79. 'ntfy:\n topic: my-unique-topic\n server: http://localhost:12345\n states: [start, finish]',
  80. START_AND_FINISH,
  81. ),
  82. (
  83. 'sentry:\n data_source_name_url: http://5f80ec@localhost:12345/203069\n monitor_slug: mymonitor',
  84. START_AND_FINISH,
  85. ),
  86. (
  87. 'uptime_kuma:\n push_url: http://localhost:12345/api/push/abcd1234',
  88. START_AND_FINISH,
  89. ),
  90. (
  91. 'zabbix:\n itemid: 1\n server: http://localhost:12345/zabbix/api_jsonrpc.php\n api_key: mykey\n states: [start, finish]',
  92. START_AND_FINISH,
  93. ),
  94. ),
  95. )
  96. def test_borgmatic_command(monitoring_hook_configuration, expected_request_count):
  97. # Create a Borg repository.
  98. temporary_directory = tempfile.mkdtemp()
  99. repository_path = os.path.join(temporary_directory, 'test.borg')
  100. extract_path = os.path.join(temporary_directory, 'extract')
  101. original_working_directory = os.getcwd()
  102. os.mkdir(extract_path)
  103. os.chdir(extract_path)
  104. try:
  105. config_path = os.path.join(temporary_directory, 'test.yaml')
  106. generate_configuration(config_path, repository_path, monitoring_hook_configuration)
  107. subprocess.check_call(
  108. f'borgmatic -v 2 --config {config_path} repo-create --encryption repokey'.split(' ')
  109. )
  110. with Background_web_server(expected_request_count):
  111. # Run borgmatic to generate a backup archive, and then list it to make sure it exists.
  112. subprocess.check_call(f'borgmatic -v 2 --config {config_path}'.split(' '))
  113. output = subprocess.check_output(
  114. f'borgmatic --config {config_path} list --json'.split(' ')
  115. ).decode(sys.stdout.encoding)
  116. parsed_output = json.loads(output)
  117. assert len(parsed_output) == 1
  118. assert len(parsed_output[0]['archives']) == 1
  119. finally:
  120. os.chdir(original_working_directory)
  121. shutil.rmtree(temporary_directory)