mirror of
https://github.com/postgrespro/pg_probackup.git
synced 2025-01-23 11:45:36 +02:00
Merge branch 'master' into pgpro-2589
This commit is contained in:
commit
cbc0ceb9b4
@ -596,7 +596,7 @@ write_backup(pgBackup *backup)
|
||||
int errno_temp;
|
||||
|
||||
pgBackupGetPath(backup, path, lengthof(path), BACKUP_CONTROL_FILE);
|
||||
snprintf(path_temp, sizeof(path_temp), "%s.partial", path);
|
||||
snprintf(path_temp, sizeof(path_temp), "%s.tmp", path);
|
||||
|
||||
fp = fopen(path_temp, "wt");
|
||||
if (fp == NULL)
|
||||
@ -637,7 +637,7 @@ write_backup_filelist(pgBackup *backup, parray *files, const char *root,
|
||||
int errno_temp;
|
||||
|
||||
pgBackupGetPath(backup, path, lengthof(path), DATABASE_FILE_LIST);
|
||||
snprintf(path_temp, sizeof(path_temp), "%s.partial", path);
|
||||
snprintf(path_temp, sizeof(path_temp), "%s.tmp", path);
|
||||
|
||||
fp = fopen(path_temp, "wt");
|
||||
if (fp == NULL)
|
||||
@ -721,7 +721,7 @@ readBackupControlFile(const char *path)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
parsed_options = config_read_opt(path, options, WARNING, true);
|
||||
parsed_options = config_read_opt(path, options, WARNING, true, true);
|
||||
|
||||
if (parsed_options == 0)
|
||||
{
|
||||
|
@ -9,6 +9,8 @@
|
||||
|
||||
#include "pg_probackup.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include "utils/configuration.h"
|
||||
#include "utils/json.h"
|
||||
|
||||
@ -213,16 +215,22 @@ do_show_config(void)
|
||||
* values into the file.
|
||||
*/
|
||||
void
|
||||
do_set_config(void)
|
||||
do_set_config(bool missing_ok)
|
||||
{
|
||||
char path[MAXPGPATH];
|
||||
char path_temp[MAXPGPATH];
|
||||
FILE *fp;
|
||||
int i;
|
||||
|
||||
join_path_components(path, backup_instance_path, BACKUP_CATALOG_CONF_FILE);
|
||||
fp = fopen(path, "wt");
|
||||
snprintf(path_temp, sizeof(path_temp), "%s.tmp", path);
|
||||
|
||||
if (!missing_ok && !fileExists(path))
|
||||
elog(ERROR, "Configuration file \"%s\" doesn't exist", path);
|
||||
|
||||
fp = fopen(path_temp, "wt");
|
||||
if (fp == NULL)
|
||||
elog(ERROR, "cannot create %s: %s",
|
||||
elog(ERROR, "Cannot create configuration file \"%s\": %s",
|
||||
BACKUP_CATALOG_CONF_FILE, strerror(errno));
|
||||
|
||||
current_group = NULL;
|
||||
@ -253,6 +261,14 @@ do_set_config(void)
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
if (rename(path_temp, path) < 0)
|
||||
{
|
||||
int errno_temp = errno;
|
||||
unlink(path_temp);
|
||||
elog(ERROR, "Cannot rename configuration file \"%s\" to \"%s\": %s",
|
||||
path_temp, path, strerror(errno_temp));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -104,7 +104,7 @@ do_add_instance(void)
|
||||
config_set_opt(instance_options, &instance_config.xlog_seg_size,
|
||||
SOURCE_FILE);
|
||||
/* pgdata was set through command line */
|
||||
do_set_config();
|
||||
do_set_config(true);
|
||||
|
||||
elog(INFO, "Instance '%s' successfully inited", instance_name);
|
||||
return 0;
|
||||
|
@ -382,8 +382,12 @@ main(int argc, char *argv[])
|
||||
config_get_opt_env(instance_options);
|
||||
|
||||
/* Read options from configuration file */
|
||||
join_path_components(path, backup_instance_path, BACKUP_CATALOG_CONF_FILE);
|
||||
config_read_opt(path, instance_options, ERROR, true);
|
||||
if (backup_subcmd != ADD_INSTANCE_CMD)
|
||||
{
|
||||
join_path_components(path, backup_instance_path,
|
||||
BACKUP_CATALOG_CONF_FILE);
|
||||
config_read_opt(path, instance_options, ERROR, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize logger */
|
||||
@ -525,7 +529,7 @@ main(int argc, char *argv[])
|
||||
do_show_config();
|
||||
break;
|
||||
case SET_CONFIG_CMD:
|
||||
do_set_config();
|
||||
do_set_config(false);
|
||||
break;
|
||||
case NO_CMD:
|
||||
/* Should not happen */
|
||||
|
@ -442,7 +442,7 @@ extern int do_archive_get(char *wal_file_path, char *wal_file_name);
|
||||
|
||||
/* in configure.c */
|
||||
extern void do_show_config(void);
|
||||
extern void do_set_config(void);
|
||||
extern void do_set_config(bool missing_ok);
|
||||
extern void init_config(InstanceConfig *config);
|
||||
|
||||
/* in show.c */
|
||||
|
@ -521,7 +521,7 @@ config_get_opt(int argc, char **argv, ConfigOption cmd_options[],
|
||||
*/
|
||||
int
|
||||
config_read_opt(const char *path, ConfigOption options[], int elevel,
|
||||
bool strict)
|
||||
bool strict, bool missing_ok)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[1024];
|
||||
@ -532,7 +532,7 @@ config_read_opt(const char *path, ConfigOption options[], int elevel,
|
||||
if (!options)
|
||||
return parsed_options;
|
||||
|
||||
if ((fp = pgut_fopen(path, "rt", true)) == NULL)
|
||||
if ((fp = pgut_fopen(path, "rt", missing_ok)) == NULL)
|
||||
return parsed_options;
|
||||
|
||||
while (fgets(buf, lengthof(buf), fp))
|
||||
|
@ -78,7 +78,7 @@ struct ConfigOption
|
||||
extern int config_get_opt(int argc, char **argv, ConfigOption cmd_options[],
|
||||
ConfigOption options[]);
|
||||
extern int config_read_opt(const char *path, ConfigOption options[], int elevel,
|
||||
bool strict);
|
||||
bool strict, bool missing_ok);
|
||||
extern void config_get_opt_env(ConfigOption options[]);
|
||||
extern void config_set_opt(ConfigOption options[], void *var,
|
||||
OptionSource source);
|
||||
|
@ -337,7 +337,13 @@ do_validate_all(void)
|
||||
sprintf(arclog_path, "%s/%s/%s", backup_path, "wal", instance_name);
|
||||
join_path_components(conf_path, backup_instance_path,
|
||||
BACKUP_CATALOG_CONF_FILE);
|
||||
config_read_opt(conf_path, instance_options, ERROR, false);
|
||||
if (config_read_opt(conf_path, instance_options, ERROR, false,
|
||||
true) == 0)
|
||||
{
|
||||
elog(WARNING, "Configuration file \"%s\" is empty", conf_path);
|
||||
corrupted_backup_found = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
do_validate_instance();
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import unittest
|
||||
|
||||
from . import init_test, merge, option_test, show_test, compatibility, \
|
||||
backup_test, delete_test, delta, restore, validate, \
|
||||
backup_test, delete, delta, restore, validate, \
|
||||
retention, pgpro560, pgpro589, pgpro2068, false_positive, replica, \
|
||||
compression, page, ptrack, archive, exclude, cfs_backup, cfs_restore, \
|
||||
cfs_validate_backup, auth_test, time_stamp, snapfs, logging, \
|
||||
@ -19,7 +19,7 @@ def load_tests(loader, tests, pattern):
|
||||
# suite.addTests(loader.loadTestsFromModule(cfs_validate_backup))
|
||||
# suite.addTests(loader.loadTestsFromModule(logging))
|
||||
suite.addTests(loader.loadTestsFromModule(compression))
|
||||
suite.addTests(loader.loadTestsFromModule(delete_test))
|
||||
suite.addTests(loader.loadTestsFromModule(delete))
|
||||
suite.addTests(loader.loadTestsFromModule(delta))
|
||||
suite.addTests(loader.loadTestsFromModule(exclude))
|
||||
suite.addTests(loader.loadTestsFromModule(false_positive))
|
||||
|
@ -250,3 +250,302 @@ class DeleteTest(ProbackupTest, unittest.TestCase):
|
||||
|
||||
# Clean after yourself
|
||||
self.del_test_dir(module_name, fname)
|
||||
|
||||
# @unittest.skip("skip")
|
||||
def test_delete_interleaved_incremental_chains(self):
|
||||
"""complicated case of interleaved backup chains"""
|
||||
fname = self.id().split('.')[3]
|
||||
node = self.make_simple_node(
|
||||
base_dir=os.path.join(module_name, fname, 'node'),
|
||||
initdb_params=['--data-checksums'])
|
||||
|
||||
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
|
||||
self.init_pb(backup_dir)
|
||||
self.add_instance(backup_dir, 'node', node)
|
||||
self.set_archiving(backup_dir, 'node', node)
|
||||
node.slow_start()
|
||||
|
||||
# Take FULL BACKUPs
|
||||
|
||||
backup_id_a = self.backup_node(backup_dir, 'node', node)
|
||||
backup_id_b = self.backup_node(backup_dir, 'node', node)
|
||||
|
||||
# Change FULL B backup status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', backup_id_b, 'ERROR')
|
||||
|
||||
# FULLb ERROR
|
||||
# FULLa OK
|
||||
# Take PAGEa1 backup
|
||||
page_id_a1 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
# PAGEa1 OK
|
||||
# FULLb ERROR
|
||||
# FULLa OK
|
||||
# Change FULL B backup status to OK
|
||||
self.change_backup_status(backup_dir, 'node', backup_id_b, 'OK')
|
||||
|
||||
# Change PAGEa1 backup status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a1, 'ERROR')
|
||||
|
||||
# PAGEa1 ERROR
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
page_id_b1 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
# PAGEb1 OK
|
||||
# PAGEa1 ERROR
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
# Now we start to play with first generation of PAGE backups
|
||||
# Change PAGEb1 status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', page_id_b1, 'ERROR')
|
||||
|
||||
# Change PAGEa1 status to OK
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a1, 'OK')
|
||||
|
||||
# PAGEb1 ERROR
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
page_id_a2 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
# PAGEa2 OK
|
||||
# PAGEb1 ERROR
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
# Change PAGEa2 status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a2, 'ERROR')
|
||||
|
||||
# Change PAGEb1 status to OK
|
||||
self.change_backup_status(backup_dir, 'node', page_id_b1, 'OK')
|
||||
|
||||
# PAGEa2 ERROR
|
||||
# PAGEb1 OK
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
page_id_b2 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
# Change PAGEa2 status to OK
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a2, 'OK')
|
||||
|
||||
# PAGEb2 OK
|
||||
# PAGEa2 OK
|
||||
# PAGEb1 OK
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
self.backup_node(backup_dir, 'node', node)
|
||||
self.backup_node(backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
# PAGEc1 OK
|
||||
# FULLc OK
|
||||
# PAGEb2 OK
|
||||
# PAGEa2 OK
|
||||
# PAGEb1 OK
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
# Delete FULLb
|
||||
self.delete_pb(
|
||||
backup_dir, 'node', backup_id_b)
|
||||
|
||||
self.assertEqual(len(self.show_pb(backup_dir, 'node')), 5)
|
||||
|
||||
print(self.show_pb(
|
||||
backup_dir, 'node', as_json=False, as_text=True))
|
||||
|
||||
# Clean after yourself
|
||||
self.del_test_dir(module_name, fname)
|
||||
|
||||
# @unittest.skip("skip")
|
||||
def test_delete_multiple_descendants(self):
|
||||
"""
|
||||
PAGEb3
|
||||
| PAGEa3
|
||||
PAGEb2 /
|
||||
| PAGEa2 /
|
||||
PAGEb1 \ /
|
||||
| PAGEa1
|
||||
FULLb |
|
||||
FULLa should be deleted
|
||||
"""
|
||||
fname = self.id().split('.')[3]
|
||||
node = self.make_simple_node(
|
||||
base_dir=os.path.join(module_name, fname, 'node'),
|
||||
initdb_params=['--data-checksums'])
|
||||
|
||||
backup_dir = os.path.join(self.tmp_path, module_name, fname, 'backup')
|
||||
self.init_pb(backup_dir)
|
||||
self.add_instance(backup_dir, 'node', node)
|
||||
self.set_archiving(backup_dir, 'node', node)
|
||||
node.slow_start()
|
||||
|
||||
node.pgbench_init(scale=3)
|
||||
|
||||
# Take FULL BACKUPs
|
||||
backup_id_a = self.backup_node(backup_dir, 'node', node)
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '2'])
|
||||
pgbench.wait()
|
||||
|
||||
backup_id_b = self.backup_node(backup_dir, 'node', node)
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '2'])
|
||||
pgbench.wait()
|
||||
|
||||
# Change FULLb backup status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', backup_id_b, 'ERROR')
|
||||
|
||||
page_id_a1 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '2'])
|
||||
pgbench.wait()
|
||||
|
||||
# Change FULLb backup status to OK
|
||||
self.change_backup_status(backup_dir, 'node', backup_id_b, 'OK')
|
||||
|
||||
# Change PAGEa1 backup status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a1, 'ERROR')
|
||||
|
||||
# PAGEa1 ERROR
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
page_id_b1 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
# PAGEb1 OK
|
||||
# PAGEa1 ERROR
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '2'])
|
||||
pgbench.wait()
|
||||
|
||||
# Change PAGEa1 backup status to OK
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a1, 'OK')
|
||||
|
||||
# Change PAGEb1 backup status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', page_id_b1, 'ERROR')
|
||||
|
||||
# PAGEb1 ERROR
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
page_id_a2 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '2'])
|
||||
pgbench.wait()
|
||||
|
||||
# PAGEa2 OK
|
||||
# PAGEb1 ERROR
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
# Change PAGEb1 backup status to OK
|
||||
self.change_backup_status(backup_dir, 'node', page_id_b1, 'OK')
|
||||
|
||||
# Change PAGEa2 backup status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a2, 'ERROR')
|
||||
|
||||
# PAGEa2 ERROR
|
||||
# PAGEb1 OK
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
page_id_b2 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '2'])
|
||||
pgbench.wait()
|
||||
|
||||
# PAGEb2 OK
|
||||
# PAGEa2 ERROR
|
||||
# PAGEb1 OK
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
# Change PAGEb2 and PAGEb1 status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', page_id_b2, 'ERROR')
|
||||
self.change_backup_status(backup_dir, 'node', page_id_b1, 'ERROR')
|
||||
|
||||
# PAGEb2 ERROR
|
||||
# PAGEa2 ERROR
|
||||
# PAGEb1 ERROR
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
page_id_a3 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
pgbench = node.pgbench(options=['-T', '10', '-c', '2'])
|
||||
pgbench.wait()
|
||||
|
||||
# PAGEa3 OK
|
||||
# PAGEb2 ERROR
|
||||
# PAGEa2 ERROR
|
||||
# PAGEb1 ERROR
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
# Change PAGEa3 status to ERROR
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a3, 'ERROR')
|
||||
|
||||
# Change PAGEb2 status to OK
|
||||
self.change_backup_status(backup_dir, 'node', page_id_b2, 'OK')
|
||||
|
||||
page_id_b3 = self.backup_node(
|
||||
backup_dir, 'node', node, backup_type='page')
|
||||
|
||||
# PAGEb3 OK
|
||||
# PAGEa3 ERROR
|
||||
# PAGEb2 OK
|
||||
# PAGEa2 ERROR
|
||||
# PAGEb1 ERROR
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
# Change PAGEa3, PAGEa2 and PAGEb1 status to OK
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a3, 'OK')
|
||||
self.change_backup_status(backup_dir, 'node', page_id_a2, 'OK')
|
||||
self.change_backup_status(backup_dir, 'node', page_id_b1, 'OK')
|
||||
|
||||
# PAGEb3 OK
|
||||
# PAGEa3 OK
|
||||
# PAGEb2 OK
|
||||
# PAGEa2 OK
|
||||
# PAGEb1 OK
|
||||
# PAGEa1 OK
|
||||
# FULLb OK
|
||||
# FULLa OK
|
||||
|
||||
# Check that page_id_a3 and page_id_a2 are both direct descendants of page_id_a1
|
||||
self.assertEqual(
|
||||
self.show_pb(backup_dir, 'node', backup_id=page_id_a3)['parent-backup-id'],
|
||||
page_id_a1)
|
||||
|
||||
self.assertEqual(
|
||||
self.show_pb(backup_dir, 'node', backup_id=page_id_a2)['parent-backup-id'],
|
||||
page_id_a1)
|
||||
|
||||
# Delete FULLa
|
||||
self.delete_pb(backup_dir, 'node', backup_id_a)
|
||||
|
||||
self.assertEqual(len(self.show_pb(backup_dir, 'node')), 4)
|
||||
|
||||
# Clean after yourself
|
||||
self.del_test_dir(module_name, fname)
|
Loading…
x
Reference in New Issue
Block a user