Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 719daaf

Browse files
author
vshepard
committed
Add readme and basic test for pg_probackup2
1 parent a388f23 commit 719daaf

File tree

3 files changed

+219
-5
lines changed

3 files changed

+219
-5
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# testgres - pg_probackup2
2+
3+
Ccontrol and testing utility for [pg_probackup2](https://github.com/postgrespro/pg_probackup). Python 3.5+ is supported.
4+
5+
6+
## Installation
7+
8+
To install `testgres`, run:
9+
10+
```
11+
pip install testgres-pg_probackup
12+
```
13+
14+
We encourage you to use `virtualenv` for your testing environment.
15+
The package requires testgres~=1.9.3.
16+
17+
## Usage
18+
19+
### Environment
20+
21+
> Note: by default testgres runs `initdb`, `pg_ctl`, `psql` provided by `PATH`.
22+
23+
There are several ways to specify a custom postgres installation:
24+
25+
* export `PG_CONFIG` environment variable pointing to the `pg_config` executable;
26+
* export `PG_BIN` environment variable pointing to the directory with executable files.
27+
28+
Example:
29+
30+
```bash
31+
export PG_BIN=$HOME/pg/bin
32+
python my_tests.py
33+
```
34+
35+
36+
### Examples
37+
38+
Here is an example of what you can do with `testgres-pg_probackup2`:
39+
40+
```python
41+
# You can see full script here plugins/pg_probackup2/pg_probackup2/tests/basic_test.py
42+
def test_full_backup(self):
43+
# Setting up a simple test node
44+
node = self.pg_node.make_simple('node', pg_options={"fsync": "off", "synchronous_commit": "off"})
45+
46+
# Initialize and configure Probackup
47+
self.pb.init()
48+
self.pb.add_instance('node', node)
49+
self.pb.set_archiving('node', node)
50+
51+
# Start the node and initialize pgbench
52+
node.slow_start()
53+
node.pgbench_init(scale=100, no_vacuum=True)
54+
55+
# Perform backup and validation
56+
backup_id = self.pb.backup_node('node', node)
57+
out = self.pb.validate('node', backup_id)
58+
59+
# Check if the backup is valid
60+
self.assertIn(f"INFO: Backup {backup_id} is valid", out)
61+
```
62+
63+
## Authors
64+
65+
[Postgres Professional](https://postgrespro.ru/about)

testgres/plugins/pg_probackup2/pg_probackup2/app.py

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
1+
import contextlib
2+
import importlib
13
import json
24
import os
35
import re
46
import subprocess
7+
import sys
8+
import threading
9+
import time
510
import unittest
611

7-
from .storage.fs_backup import TestBackupDir
12+
import testgres
13+
14+
from .storage.fs_backup import TestBackupDir, FSTestBackupDir
815
from .gdb import GDBobj
916
from .init_helpers import init_params
1017

@@ -271,14 +278,14 @@ def backup_replica_node(self, instance, node, data_dir=False, *,
271278
# we need to break on wait_wal_lsn in pg_stop_backup
272279
gdb.run_until_break()
273280
if backup_type == 'page':
274-
self.test_class.switch_wal_segment(master)
281+
self.switch_wal_segment(master)
275282
if '--stream' not in options:
276283
gdb.continue_execution_until_break()
277-
self.test_class.switch_wal_segment(master)
284+
self.switch_wal_segment(master)
278285
gdb.continue_execution_until_exit()
279286

280-
output = self.test_class.read_pb_log()
281-
self.test_class.unlink_pg_log()
287+
output = self.read_pb_log()
288+
self.unlink_pg_log()
282289
parsed_output = re.compile(r'Backup \S+ completed').search(output)
283290
assert parsed_output, f"Expected: `Backup 'backup_id' completed`, but found `{output}`"
284291
backup_id = parsed_output[0].split(' ')[1]
@@ -693,3 +700,66 @@ def set_archiving(
693700
options['archive_command'] = archive_command
694701

695702
node.set_auto_conf(options)
703+
704+
def switch_wal_segment(self, node, sleep_seconds=1, and_tx=False):
705+
"""
706+
Execute pg_switch_wal() in given node
707+
708+
Args:
709+
node: an instance of PostgresNode or NodeConnection class
710+
"""
711+
if isinstance(node, testgres.PostgresNode):
712+
with node.connect('postgres') as con:
713+
if and_tx:
714+
con.execute('select txid_current()')
715+
lsn = con.execute('select pg_switch_wal()')[0][0]
716+
else:
717+
lsn = node.execute('select pg_switch_wal()')[0][0]
718+
719+
if sleep_seconds > 0:
720+
time.sleep(sleep_seconds)
721+
return lsn
722+
723+
@contextlib.contextmanager
724+
def switch_wal_after(self, node, seconds, and_tx=True):
725+
tm = threading.Timer(seconds, self.switch_wal_segment, [node, 0, and_tx])
726+
tm.start()
727+
try:
728+
yield
729+
finally:
730+
tm.cancel()
731+
tm.join()
732+
733+
def read_pb_log(self):
734+
with open(os.path.join(self.pb_log_path, 'pg_probackup.log')) as fl:
735+
return fl.read()
736+
737+
def unlink_pg_log(self):
738+
os.unlink(os.path.join(self.pb_log_path, 'pg_probackup.log'))
739+
740+
def load_backup_class(fs_type):
741+
fs_type = os.environ.get('PROBACKUP_FS_TYPE')
742+
implementation = f"{__package__}.fs_backup.FSTestBackupDir"
743+
if fs_type:
744+
implementation = fs_type
745+
746+
print("Using ", implementation)
747+
module_name, class_name = implementation.rsplit(sep='.', maxsplit=1)
748+
749+
module = importlib.import_module(module_name)
750+
751+
return getattr(module, class_name)
752+
753+
754+
# Local or S3 backup
755+
fs_backup_class = FSTestBackupDir
756+
if os.environ.get('PG_PROBACKUP_S3_TEST', os.environ.get('PROBACKUP_S3_TYPE_FULL_TEST')):
757+
root = os.path.realpath(os.path.join(os.path.dirname(__file__), '../..'))
758+
if root not in sys.path:
759+
sys.path.append(root)
760+
from pg_probackup2.storage.s3_backup import S3TestBackupDir
761+
fs_backup_class = S3TestBackupDir
762+
763+
def build_backup_dir(self, backup='backup'):
764+
return fs_backup_class(rel_path=self.rel_path, backup=backup)
765+
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import os
2+
import shutil
3+
import unittest
4+
import testgres
5+
from pg_probackup2.app import ProbackupApp
6+
from pg_probackup2.init_helpers import Init, init_params
7+
from pg_probackup2.app import build_backup_dir
8+
9+
10+
class TestUtils:
11+
@staticmethod
12+
def get_module_and_function_name(test_id):
13+
try:
14+
module_name = test_id.split('.')[-2]
15+
fname = test_id.split('.')[-1]
16+
except IndexError:
17+
print(f"Couldn't get module name and function name from test_id: `{test_id}`")
18+
module_name, fname = test_id.split('(')[1].split('.')[1], test_id.split('(')[0]
19+
return module_name, fname
20+
21+
22+
class ProbackupTest(unittest.TestCase):
23+
def setUp(self):
24+
self.setup_test_environment()
25+
self.setup_test_paths()
26+
self.setup_backup_dir()
27+
self.setup_probackup()
28+
29+
def setup_test_environment(self):
30+
self.output = None
31+
self.cmd = None
32+
self.nodes_to_cleanup = []
33+
self.module_name, self.fname = TestUtils.get_module_and_function_name(self.id())
34+
self.test_env = Init().test_env()
35+
36+
def setup_test_paths(self):
37+
self.rel_path = os.path.join(self.module_name, self.fname)
38+
self.test_path = os.path.join(init_params.tmp_path, self.rel_path)
39+
os.makedirs(self.test_path)
40+
self.pb_log_path = os.path.join(self.test_path, "pb_log")
41+
42+
def setup_backup_dir(self):
43+
self.backup_dir = build_backup_dir(self, 'backup')
44+
self.backup_dir.cleanup()
45+
46+
def setup_probackup(self):
47+
self.pg_node = testgres.NodeApp(self.test_path, self.nodes_to_cleanup)
48+
self.pb = ProbackupApp(self, self.pg_node, self.pb_log_path, self.test_env,
49+
auto_compress_alg='zlib', backup_dir=self.backup_dir)
50+
51+
def tearDown(self):
52+
if os.path.exists(self.test_path):
53+
shutil.rmtree(self.test_path)
54+
55+
56+
class BasicTest(ProbackupTest):
57+
def test_full_backup(self):
58+
# Setting up a simple test node
59+
node = self.pg_node.make_simple('node', pg_options={"fsync": "off", "synchronous_commit": "off"})
60+
61+
# Initialize and configure Probackup
62+
self.pb.init()
63+
self.pb.add_instance('node', node)
64+
self.pb.set_archiving('node', node)
65+
66+
# Start the node and initialize pgbench
67+
node.slow_start()
68+
node.pgbench_init(scale=100, no_vacuum=True)
69+
70+
# Perform backup and validation
71+
backup_id = self.pb.backup_node('node', node)
72+
out = self.pb.validate('node', backup_id)
73+
74+
# Check if the backup is valid
75+
self.assertIn(f"INFO: Backup {backup_id} is valid", out)
76+
77+
78+
if __name__ == "__main__":
79+
unittest.main()

0 commit comments

Comments
 (0)