Skip to content

Commit a995b19

Browse files
committed
Merge branch 'master' of github.com:postgrespro/testgres
2 parents 6b4b0ad + 21437ad commit a995b19

File tree

4 files changed

+79
-30
lines changed

4 files changed

+79
-30
lines changed

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,23 @@ node.stop()
8585

8686
It is essential to clean everything up, so make sure to call `node.cleanup()` once you've finished all of your tests.
8787

88+
Nodes support python logging system, so if you have configured logging
89+
in your tests you can use it to redirect postgres logs to yours.
90+
To do that just use `use_logging` argument like here:
91+
92+
```python
93+
node = testgres.get_new_node('master', use_logging=True)
94+
```
95+
96+
Also you can find working configuration sample for logging in tests.
97+
8898
Please see `testgres/tests` directory for replication configuration example.
8999
> Note: you could take a look at [`pg_pathman`](https://github.com/postgrespro/pg_pathman) to get an idea of `testgres`' capabilities.
90100
91101

92102
## Authors
93103

94-
Ildar Musin <i.musin(at)postgrespro.ru> Postgres Professional Ltd., Russia
95-
Dmitry Ivanov <d.ivanov(at)postgrespro.ru> Postgres Professional Ltd., Russia
96-
Ildus Kurbangaliev <i.kurbangaliev(at)postgrespro.ru> Postgres Professional Ltd., Russia
104+
[Ildar Musin](https://github.com/zilder) <i.musin(at)postgrespro.ru> Postgres Professional Ltd., Russia
105+
[Dmitry Ivanov](https://github.com/funbringer) <d.ivanov(at)postgrespro.ru> Postgres Professional Ltd., Russia
106+
[Ildus Kurbangaliev](https://github.com/ildus) <i.kurbangaliev(at)postgrespro.ru> Postgres Professional Ltd., Russia
107+
[Yury Zhuravlev](https://github.com/stalkerg) <stalkerg(at)gmail.com>

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
setup(
33
name='testgres',
44
packages=['testgres'],
5-
version='0.3.0',
5+
version='0.3.1',
66
description='Testing utility for postgresql and its extensions',
77
license='PostgreSQL',
88
author='Ildar Musin',

testgres/testgres.py

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@
5050

5151
registered_nodes = []
5252
util_threads = []
53+
tmp_dirs = []
5354
last_assigned_port = int(random.random() * 16384) + 49152
5455
pg_config_data = {}
56+
base_data_dir = None
5557

5658

5759
class ClusterException(Exception):
@@ -208,6 +210,7 @@ def __init__(self, name, port, base_dir=None, use_logging=False):
208210
self.port = port
209211
if base_dir is None:
210212
self.base_dir = tempfile.mkdtemp()
213+
tmp_dirs.append(self.base_dir)
211214
else:
212215
self.base_dir = base_dir
213216
if not os.path.exists(self.logs_dir):
@@ -246,6 +249,31 @@ def get_bin_path(self, filename):
246249
else:
247250
return os.path.join(pg_config.get("BINDIR"), filename)
248251

252+
def initdb(self, directory, initdb_params=[]):
253+
initdb = self.get_bin_path("initdb")
254+
initdb_logfile = os.path.join(self.logs_dir, "initdb.log")
255+
256+
with open(initdb_logfile, 'a') as file_out:
257+
ret = subprocess.call(
258+
[initdb, directory, "-N"] + initdb_params,
259+
stdout=file_out,
260+
stderr=subprocess.STDOUT)
261+
262+
if ret:
263+
raise ClusterException("Cluster initialization failed. You"
264+
" can find additional information at '%s'" % initdb_logfile)
265+
266+
def _setup_data_dir(self, data_dir):
267+
global base_data_dir
268+
269+
if base_data_dir is None:
270+
base_data_dir = tempfile.mkdtemp()
271+
tmp_dirs.append(base_data_dir)
272+
self.initdb(base_data_dir)
273+
274+
shutil.copytree(base_data_dir, data_dir)
275+
276+
249277
def init(self, allows_streaming=False, initdb_params=[]):
250278
""" Performs initdb """
251279

@@ -258,19 +286,13 @@ def init(self, allows_streaming=False, initdb_params=[]):
258286
return self
259287

260288
# initialize cluster
261-
os.makedirs(self.data_dir)
262-
initdb = self.get_bin_path("initdb")
263-
with open(os.path.join(self.logs_dir, "initdb.log"), "a") as file_out:
264-
ret = subprocess.call(
265-
[initdb, self.data_dir, "-N"] + initdb_params,
266-
stdout=file_out,
267-
stderr=subprocess.STDOUT
268-
)
269-
if ret:
270-
raise ClusterException("Cluster initialization failed")
289+
if initdb_params:
290+
self.initdb(self.data_dir, initdb_params)
291+
else:
292+
self._setup_data_dir(self.data_dir)
271293

272294
# add parameters to config file
273-
with open(postgres_conf, "a") as conf:
295+
with open(postgres_conf, "w") as conf:
274296
conf.write(
275297
"fsync = off\n"
276298
"log_statement = all\n"
@@ -286,7 +308,6 @@ def init(self, allows_streaming=False, initdb_params=[]):
286308
"max_wal_senders = 5\n"
287309
"wal_keep_segments = 20\n"
288310
"max_wal_size = 128MB\n"
289-
"shared_buffers = 1MB\n"
290311
"wal_log_hints = on\n"
291312
"hot_standby = on\n"
292313
"max_connections = 10\n")
@@ -369,7 +390,9 @@ def pg_ctl(self, command, params={}, command_options=[]):
369390

370391
if res > 0:
371392
with open(self.error_filename, "r") as errfile:
372-
raise ClusterException(errfile.readlines()[-1])
393+
text = errfile.readlines()[-1]
394+
text += 'Logs at: %s' % self.logs_dir
395+
raise ClusterException(text)
373396

374397
def start(self, params={}):
375398
""" Starts cluster """
@@ -434,7 +457,7 @@ def get_control_data(self):
434457
lines = subprocess.check_output(
435458
[pg_controldata] + ["-D", self.data_dir],
436459
stderr=subprocess.STDOUT
437-
).splitlines()
460+
).decode("utf-8").splitlines()
438461
except subprocess.CalledProcessError as e:
439462
raise PgcontroldataException(e.output, e.cmd)
440463

@@ -489,8 +512,7 @@ def cleanup(self):
489512
pass
490513

491514
# remove data directory
492-
shutil.rmtree(self.data_dir)
493-
515+
shutil.rmtree(self.data_dir, ignore_errors=True)
494516
return self
495517

496518
def psql(self, dbname, query=None, filename=None, username=None):
@@ -597,7 +619,7 @@ def backup(self, name):
597619
backup_path = os.path.join(self.base_dir, name)
598620
os.makedirs(backup_path)
599621
params = [pg_basebackup, "-D", backup_path, "-p {}".format(
600-
self.port), "-x"]
622+
self.port), "-X", "fetch"]
601623
with open(self.output_filename, "a") as file_out, \
602624
open(self.error_filename, "a") as file_err:
603625
ret = subprocess.call(
@@ -726,7 +748,16 @@ def clean_all():
726748
global registered_nodes
727749
for node in registered_nodes:
728750
node.cleanup()
751+
752+
for cat in tmp_dirs:
753+
if os.path.exists():
754+
shutil.rmtree(cat, ignore_errors=True)
755+
756+
if cat == base_data_dir:
757+
base_data_dir = None
758+
729759
registered_nodes = []
760+
tmp_dirs = []
730761

731762

732763
def stop_all():

testgres/tests/test_simple.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66
import tempfile
77
import logging.config
88

9-
from testgres import get_new_node, stop_all
9+
from testgres import get_new_node, stop_all, get_config, clean_all
1010

1111

1212
class SimpleTest(unittest.TestCase):
1313

1414
def teardown(self):
15-
# clean_all()
15+
clean_all()
1616
stop_all()
1717

1818
@unittest.skip("demo")
@@ -41,15 +41,21 @@ def test_backup_and_replication(self):
4141
self.assertEqual(len(res), 1)
4242
self.assertEqual(res[0], (1, 2))
4343

44+
# Prepare the query which would check whether record reached replica
45+
# (It is slightly different for Postgres 9.6 and Postgres 10+)
46+
if get_config()['VERSION_NUM'] >= 1000000:
47+
wait_lsn = 'SELECT pg_current_wal_lsn() <= replay_lsn ' \
48+
'FROM pg_stat_replication WHERE application_name = \'%s\'' \
49+
% replica.name
50+
else:
51+
wait_lsn = 'SELECT pg_current_xlog_location() <= replay_location '\
52+
'FROM pg_stat_replication WHERE application_name = \'%s\'' \
53+
% replica.name
54+
4455
# Insert into master node
4556
node.psql('postgres', 'insert into abc values (3, 4)')
4657
# Wait until data syncronizes
47-
node.poll_query_until(
48-
'postgres',
49-
'SELECT pg_current_xlog_location() <= replay_location '
50-
'FROM pg_stat_replication WHERE application_name = \'%s\''
51-
% replica.name)
52-
# time.sleep(0.5)
58+
node.poll_query_until('postgres', wait_lsn)
5359
# Check that this record was exported to replica
5460
res = replica.execute('postgres', 'select * from abc')
5561
self.assertEqual(len(res), 2)
@@ -81,9 +87,10 @@ def test_users(self):
8187
node.psql('postgres', 'create role test_user login')
8288
value = node.safe_psql('postgres', 'select 1', username='test_user')
8389
self.assertEqual(value, six.b('1\n'))
90+
node.stop()
8491

8592
def test_logging(self):
86-
regex = re.compile('\w+:\s{1}LOG:.*')
93+
regex = re.compile('.+?LOG:.*')
8794
logfile = tempfile.NamedTemporaryFile('w', delete=True)
8895

8996
log_conf = {

0 commit comments

Comments
 (0)