1
0
mirror of https://github.com/postgrespro/pg_probackup.git synced 2025-02-03 14:01:57 +02:00

Start working on testgres framework for pg_probackup.

This commit is contained in:
stalkerg 2016-12-09 21:30:22 +03:00
parent 6642f27cca
commit e7d6a08bf3
8 changed files with 384 additions and 3 deletions

View File

@ -827,7 +827,7 @@ parse_pair(const char buffer[], char key[], char value[])
if (end - start <= 0)
{
if (*start == '=')
elog(WARNING, "syntax error in \"%s\"", buffer);
elog(ERROR, "syntax error in \"%s\"", buffer);
return false;
}
@ -841,7 +841,7 @@ parse_pair(const char buffer[], char key[], char value[])
if (*start != '=')
{
elog(WARNING, "syntax error in \"%s\"", buffer);
elog(ERROR, "syntax error in \"%s\"", buffer);
return false;
}
@ -858,7 +858,7 @@ parse_pair(const char buffer[], char key[], char value[])
if (*start != '\0' && *start != '#')
{
elog(WARNING, "syntax error in \"%s\"", buffer);
elog(ERROR, "syntax error in \"%s\"", buffer);
return false;
}

12
tests/__init__.py Normal file
View File

@ -0,0 +1,12 @@
import unittest
from . import init_test, option_test, show_test
def load_tests(loader, tests, pattern):
suite = unittest.TestSuite()
suite.addTests(loader.loadTestsFromModule(init_test))
suite.addTests(loader.loadTestsFromModule(option_test))
suite.addTests(loader.loadTestsFromModule(show_test))
return suite

View File

@ -0,0 +1,51 @@
pg_probackup manage backup/recovery of PostgreSQL database.
Usage:
pg_probackup [option...] init
pg_probackup [option...] backup
pg_probackup [option...] restore
pg_probackup [option...] show [backup-ID]
pg_probackup [option...] validate backup-ID
pg_probackup [option...] delete backup-ID
pg_probackup [option...] delwal [backup-ID]
Common Options:
-B, --backup-path=PATH location of the backup storage area
-D, --pgdata=PATH location of the database storage area
Backup options:
-b, --backup-mode=MODE backup mode (full, page, ptrack)
-C, --smooth-checkpoint do smooth checkpoint before backup
--stream stream the transaction log and include it in the backup
-S, --slot=SLOTNAME replication slot to use
--backup-pg-log backup of pg_log directory
-j, --threads=NUM number of parallel threads
--progress show progress
Restore options:
--time time stamp up to which recovery will proceed
--xid transaction ID up to which recovery will proceed
--inclusive whether we stop just after the recovery target
--timeline recovering into a particular timeline
-j, --threads=NUM number of parallel threads
--progress show progress
Delete options:
--wal remove unnecessary wal files
Connection options:
-d, --dbname=DBNAME database to connect
-h, --host=HOSTNAME database server host or socket directory
-p, --port=PORT database server port
-U, --username=USERNAME user name to connect as
-w, --no-password never prompt for password
-W, --password force password prompt
Generic options:
-q, --quiet don't write any messages
-v, --verbose verbose mode
--help show this help, then exit
--version output version information and exit
Read the website for details. <https://github.com/postgrespro/pg_probackup>
Report bugs to <https://github.com/postgrespro/pg_probackup/issues>.

View File

@ -0,0 +1 @@
pg_probackup 1.0

41
tests/init_test.py Normal file
View File

@ -0,0 +1,41 @@
import unittest
import os
from os import path
import six
from .pb_lib import dir_files, ProbackupTest
class InitTest(ProbackupTest, unittest.TestCase):
def __init__(self, *args, **kwargs):
super(InitTest, self).__init__(*args, **kwargs)
def test_success_1(self):
"""Success normal init"""
node = self.make_bnode('test_success_1', base_dir="tmp_dirs/init/success_1")
self.assertEqual(self.init_pb(node), six.b(""))
self.assertEqual(
dir_files(self.backup_dir(node)),
['backups', 'pg_probackup.conf', 'wal']
)
def test_already_exist_2(self):
"""Failure with backup catalog already existed"""
node = self.make_bnode('test_already_exist_2', base_dir="tmp_dirs/init/already_exist_2")
self.init_pb(node)
self.assertEqual(
self.init_pb(node),
six.b("ERROR: backup catalog already exist and it's not empty\n")
)
def test_abs_path_3(self):
"""failure with backup catalog should be given as absolute path"""
node = self.make_bnode('test_abs_path_3', base_dir="tmp_dirs/init/abs_path_3")
self.assertEqual(
self.run_pb(["init", "-B", path.relpath("%s/backup" % node.base_dir, self.dir_path)]),
six.b("ERROR: -B, --backup-path must be an absolute path\n")
)
if __name__ == '__main__':
unittest.main()

118
tests/option_test.py Normal file
View File

@ -0,0 +1,118 @@
import unittest
from os import path
import six
from .pb_lib import ProbackupTest
from testgres import stop_all
class OptionTest(ProbackupTest, unittest.TestCase):
def __init__(self, *args, **kwargs):
super(OptionTest, self).__init__(*args, **kwargs)
@classmethod
def tearDownClass(cls):
stop_all()
def test_help_1(self):
"""help options"""
with open(path.join(self.dir_path, "expected/option_help.out"), "rb") as help_out:
self.assertEqual(
self.run_pb(["--help"]),
help_out.read()
)
def test_version_2(self):
"""help options"""
with open(path.join(self.dir_path, "expected/option_version.out"), "rb") as version_out:
self.assertEqual(
self.run_pb(["--version"]),
version_out.read()
)
def test_without_backup_path_3(self):
"""backup command failure without backup mode option"""
self.assertEqual(
self.run_pb(["backup", "-b", "full"]),
six.b("ERROR: required parameter not specified: BACKUP_PATH (-B, --backup-path)\n")
)
def test_options_4(self):
node = self.make_bnode('test_options_4', base_dir="tmp_dirs/option/option_common")
try:
node.stop()
except:
pass
self.assertEqual(self.init_pb(node), six.b(""))
# backup command failure without backup mode option
self.assertEqual(
self.run_pb(["backup", "-B", self.backup_dir(node), "-D", node.data_dir]),
six.b("ERROR: Required parameter not specified: BACKUP_MODE (-b, --backup-mode)\n")
)
# backup command failure with invalid backup mode option
self.assertEqual(
self.run_pb(["backup", "-b", "bad", "-B", self.backup_dir(node)]),
six.b('ERROR: invalid backup-mode "bad"\n')
)
# delete failure without ID
self.assertEqual(
self.run_pb(["delete", "-B", self.backup_dir(node)]),
six.b("ERROR: required backup ID not specified\n")
)
node.start()
# syntax error in pg_probackup.conf
with open(path.join(self.backup_dir(node), "pg_probackup.conf"), "a") as conf:
conf.write(" = INFINITE\n")
self.assertEqual(
self.backup_pb(node),
six.b('ERROR: syntax error in " = INFINITE"\n')
)
self.clean_pb(node)
self.assertEqual(self.init_pb(node), six.b(""))
# invalid value in pg_probackup.conf
with open(path.join(self.backup_dir(node), "pg_probackup.conf"), "a") as conf:
conf.write("BACKUP_MODE=\n")
self.assertEqual(
self.backup_pb(node, backup_type=None),
six.b('ERROR: invalid backup-mode ""\n')
)
self.clean_pb(node)
self.assertEqual(self.init_pb(node), six.b(""))
# TODO: keep data generations
# invalid value in pg_probackup.conf
with open(path.join(self.backup_dir(node), "pg_probackup.conf"), "a") as conf:
conf.write("SMOOTH_CHECKPOINT=FOO\n")
self.assertEqual(
self.backup_pb(node),
six.b("ERROR: option -C, --smooth-checkpoint should be a boolean: 'FOO'\n")
)
self.clean_pb(node)
self.assertEqual(self.init_pb(node), six.b(""))
# invalid option in pg_probackup.conf
with open(path.join(self.backup_dir(node), "pg_probackup.conf"), "a") as conf:
conf.write("TIMELINEID=1\n")
self.assertEqual(
self.backup_pb(node),
six.b('ERROR: invalid option "TIMELINEID"\n')
)
self.clean_pb(node)
self.assertEqual(self.init_pb(node), six.b(""))
node.stop()

109
tests/pb_lib.py Normal file
View File

@ -0,0 +1,109 @@
import os
from os import path
import subprocess
import shutil
import six
from testgres import get_new_node
def dir_files(base_dir):
out_list = []
for dir_name, subdir_list, file_list in os.walk(base_dir):
if dir_name != base_dir:
out_list.append(path.relpath(dir_name, base_dir))
for fname in file_list:
out_list.append(path.relpath(path.join(dir_name, fname), base_dir))
out_list.sort()
return out_list
class ProbackupTest(object):
def __init__(self, *args, **kwargs):
super(ProbackupTest, self).__init__(*args, **kwargs)
self.dir_path = path.dirname(os.path.realpath(__file__))
try:
os.makedirs(path.join(self.dir_path, "tmp_dirs"))
except:
pass
self.probackup_path = os.path.abspath(path.join(
self.dir_path,
"../pg_probackup"
))
def arcwal_dir(self, node):
return "%s/backup/wal" % node.base_dir
def backup_dir(self, node):
return os.path.abspath("%s/backup" % node.base_dir)
def make_bnode(self, name, base_dir=None):
node = get_new_node('test', base_dir=path.join(self.dir_path, base_dir))
try:
node.cleanup()
except:
pass
shutil.rmtree(self.backup_dir(node), ignore_errors=True)
node.init()
node.append_conf("postgresql.conf", "wal_level = hot_standby")
node.append_conf("postgresql.conf", "archive_mode = on")
node.append_conf(
"postgresql.conf",
"""archive_command = 'cp "%%p" "%s/%%f"'""" % os.path.abspath(self.arcwal_dir(node))
)
return node
def run_pb(self, command):
try:
return subprocess.check_output(
[self.probackup_path] + command,
stderr=subprocess.STDOUT
)
except subprocess.CalledProcessError as err:
return err.output
def init_pb(self, node):
return self.run_pb([
"init",
"-B", self.backup_dir(node),
"-D", node.data_dir
])
def clean_pb(self, node):
shutil.rmtree(self.backup_dir(node), ignore_errors=True)
def backup_pb(self, node, backup_type="full", options=[]):
cmd_list = [
"backup",
"-D", node.data_dir,
"-B", self.backup_dir(node),
"-p", "%i" % node.port,
"-d", "postgres"
]
if backup_type:
cmd_list += ["-b", backup_type]
# print(cmd_list)
return self.run_pb(cmd_list + options)
def show_pb(self, node, id=None, options=[]):
cmd_list = [
"-B", self.backup_dir(node),
"show",
]
if id:
cmd_list += [id]
# print(cmd_list)
return self.run_pb(options + cmd_list)
def validate_pb(self, node, id=None, options=[]):
cmd_list = [
"-B", self.backup_dir(node),
"validate",
]
if id:
cmd_list += [id]
# print(cmd_list)
return self.run_pb(options + cmd_list)

49
tests/show_test.py Normal file
View File

@ -0,0 +1,49 @@
import unittest
import os
from os import path
import six
from .pb_lib import ProbackupTest
from testgres import stop_all
class OptionTest(ProbackupTest, unittest.TestCase):
def __init__(self, *args, **kwargs):
super(OptionTest, self).__init__(*args, **kwargs)
@classmethod
def tearDownClass(cls):
stop_all()
def test_ok_1(self):
"""Status DONE and OK"""
node = self.make_bnode('done_ok', base_dir="tmp_dirs/show/ok_1")
node.start()
self.assertEqual(self.init_pb(node), six.b(""))
self.assertEqual(
self.backup_pb(node, options=["--quiet"]),
six.b("")
)
self.assertIn(six.b("OK"), self.show_pb(node))
node.stop()
def test_corrupt_2(self):
"""Status DONE and OK"""
node = self.make_bnode('corrupt', base_dir="tmp_dirs/show/corrupt_2")
node.start()
self.assertEqual(self.init_pb(node), six.b(""))
self.assertEqual(
self.backup_pb(node, options=["--quiet"]),
six.b("")
)
id_backup = self.show_pb(node).splitlines()[3].split()[0]
os.remove(path.join(self.backup_dir(node), "backups", id_backup.decode("utf-8"), "database", "postgresql.conf"))
self.validate_pb(node, id_backup)
self.assertIn(six.b("CORRUPT"), self.show_pb(node))
node.stop()