From a1ad62d04ef49edf90159a558a37a1fcf35cd854 Mon Sep 17 00:00:00 2001 From: Ian Barwick Date: Tue, 1 Aug 2017 22:13:32 +0900 Subject: [PATCH] Add "repmgr node restore-config" --- repmgr-action-node.c | 123 +++++++++++++++++++++++++++++++++++++++---- repmgr-client.c | 24 +++++++-- 2 files changed, 134 insertions(+), 13 deletions(-) diff --git a/repmgr-action-node.c b/repmgr-action-node.c index 908980cf..0a2a77f2 100644 --- a/repmgr-action-node.c +++ b/repmgr-action-node.c @@ -15,6 +15,7 @@ #include "repmgr-action-node.h" static bool copy_file(const char *src_file, const char *dest_file); +static void format_archive_dir(char *archive_dir); void do_node_status(void) @@ -282,12 +283,12 @@ do_node_check(void) /* - * Intended mainly for "internal" use by `standby switchover`, which + * Intended mainly for "internal" use by `node switchover`, which * calls this on the target server to archive any configuration files * in the data directory, which may be overwritten by an operation * like pg_rewind * - * Requires configuration file. + * Requires configuration file, optionally --config_archive_dir */ void do_node_archive_config(void) @@ -302,13 +303,7 @@ do_node_archive_config(void) KeyValueListCell *cell; int copied_count = 0; - snprintf(archive_dir, - MAXPGPATH, - "%s/repmgr-config-archive-%s", - runtime_options.config_archive_dir, - config_file_options.node_name); - - log_verbose(LOG_DEBUG, "using archive directory \"%s\"", archive_dir); + format_archive_dir(archive_dir); /* sanity-check directory path */ if (stat(archive_dir, &statbuf) == -1) @@ -317,7 +312,7 @@ do_node_archive_config(void) { log_error(_("error encountered when checking archive directory \"%s\""), archive_dir); - log_detail("%s",strerror(errno)); + log_detail("%s", strerror(errno)); exit(ERR_BAD_CONFIG); } @@ -406,12 +401,120 @@ do_node_archive_config(void) } +/* + * Intended mainly for "internal" use by `standby switchover`, which + * calls this on the target server to restore any configuration files + * to the data directory, which may have been overwritten by an operation + * like pg_rewind + * + * Not designed to be called if the instance is running, but does + * not currently check. + * + * Requires -D/--pgdata, optionally --config_archive_dir + * + * Removes --config_archive_dir after successful copy + */ + void do_node_restore_config(void) { + char archive_dir[MAXPGPATH]; + + DIR *arcdir; + struct dirent *arcdir_ent; + int copied_count = 0; + bool copy_ok = true; + + format_archive_dir(archive_dir); + + arcdir = opendir(archive_dir); + + if (arcdir == NULL) + { + log_error(_("unable to open archive directory \"%s\""), + archive_dir); + log_detail("%s", strerror(errno)); + exit(ERR_BAD_CONFIG); + } + + while ((arcdir_ent = readdir(arcdir)) != NULL) { + struct stat statbuf; + char src_file_path[MAXPGPATH]; + char dest_file_path[MAXPGPATH]; + + snprintf(src_file_path, MAXPGPATH, + "%s/%s", + archive_dir, + arcdir_ent->d_name); + + /* skip non-files */ + if (stat(src_file_path, &statbuf) == 0 && !S_ISREG(statbuf.st_mode)) + { + continue; + } + + snprintf(dest_file_path, MAXPGPATH, + "%s/%s", + runtime_options.data_dir, + arcdir_ent->d_name); + + log_verbose(LOG_DEBUG, "copying \"%s\" to \"%s\"", src_file_path, dest_file_path); + + if (copy_file(src_file_path, dest_file_path) == false) + { + copy_ok = false; + log_warning(_("unable to copy \"%s\" to \"%s\""), + arcdir_ent->d_name, runtime_options.data_dir); + } + else + { + unlink(src_file_path); + copied_count++; + } + + } + closedir(arcdir); + + + if (copy_ok == false) + { + log_error(_("unable to copy all files from %s"), archive_dir); + exit(ERR_BAD_CONFIG); + } + + log_notice(_("%i files copied to %s"), copied_count, runtime_options.data_dir); + + /* + * Finally, delete directory - it should be empty unless it's been interfered + * with for some reason, in which case manual intervention is required + */ + if (rmdir(archive_dir) != 0 && errno != EEXIST) + { + log_warning(_("unable to delete %s"), archive_dir); + log_detail(_("directory may need to be manually removed")); + } + else + { + log_verbose(LOG_NOTICE, "directory %s deleted", archive_dir); + } + return; } + +static void +format_archive_dir(char *archive_dir) +{ + snprintf(archive_dir, + MAXPGPATH, + "%s/repmgr-config-archive-%s", + runtime_options.config_archive_dir, + config_file_options.node_name); + + log_verbose(LOG_DEBUG, "using archive directory \"%s\"", archive_dir); +} + + static bool copy_file(const char *src_file, const char *dest_file) { diff --git a/repmgr-client.c b/repmgr-client.c index c9f63ede..cb035800 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -1086,7 +1086,8 @@ check_cli_parameters(const int action) } } } - break; + break; + case STANDBY_FOLLOW: { /* @@ -1101,8 +1102,20 @@ check_cli_parameters(const int action) _("-D/--pgdata required when providing connection parameters for \"standby follow\"")); } } - } + break; + + case NODE_RESTORE_CONFIG: + { + if (strcmp(runtime_options.data_dir, "") == 0) + { + item_list_append(&cli_errors, _("-D/--pgdata required when executing NODE RESTORE-CONFIG")); + } + + config_file_required = false; + } + break; + case CLUSTER_SHOW: case CLUSTER_MATRIX: case CLUSTER_CROSSCHECK: @@ -1311,6 +1324,12 @@ action_name(const int action) case NODE_STATUS: return "NODE STATUS"; + case NODE_CHECK: + return "NODE CHECK"; + case NODE_ARCHIVE_CONFIG: + return "NODE ARCHIVE-CONFIG"; + case NODE_RESTORE_CONFIG: + return "NODE RESTORE-CONFIG"; case CLUSTER_SHOW: return "CLUSTER SHOW"; @@ -1320,7 +1339,6 @@ action_name(const int action) return "CLUSTER MATRIX"; case CLUSTER_CROSSCHECK: return "CLUSTER CROSSCHECK"; - } return "UNKNOWN ACTION";