diff --git a/Makefile.global.in b/Makefile.global.in index 83f875ab..775a9996 100644 --- a/Makefile.global.in +++ b/Makefile.global.in @@ -22,7 +22,7 @@ GIT_WORK_TREE=${repmgr_abs_srcdir} GIT_DIR=${repmgr_abs_srcdir}/.git export GIT_DIR export GIT_WORK_TREE - +PG_LDFLAGS=-lcurl -ljson-c include $(PGXS) -include ${repmgr_abs_srcdir}/Makefile.custom diff --git a/Makefile.in b/Makefile.in index cf6a5ad5..ef048f3e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -66,7 +66,7 @@ REPMGR_CLIENT_OBJS = repmgr-client.o \ repmgr-action-primary.o repmgr-action-standby.o repmgr-action-witness.o \ repmgr-action-cluster.o repmgr-action-node.o repmgr-action-service.o repmgr-action-daemon.o \ configdata.o configfile.o configfile-scan.o log.o strutil.o controldata.o dirutil.o compat.o \ - dbutils.o sysutils.o + dbutils.o sysutils.o pgbackupapi.o REPMGRD_OBJS = repmgrd.o repmgrd-physical.o configdata.o configfile.o configfile-scan.o log.o \ dbutils.o strutil.o controldata.o compat.o sysutils.o diff --git a/configdata.c b/configdata.c index d40d158a..466cc810 100644 --- a/configdata.c +++ b/configdata.c @@ -291,6 +291,46 @@ struct ConfigFileSetting config_file_settings[] = {}, {} }, + /* pg_backupapi_backup_id*/ + { + "pg_backupapi_backup_id", + CONFIG_STRING, + { .strptr = config_file_options.pg_backupapi_backup_id }, + { .strdefault = "" }, + {}, + { .strmaxlen = sizeof(config_file_options.pg_backupapi_backup_id) }, + {} + }, + /* pg_backupapi_host*/ + { + "pg_backupapi_host", + CONFIG_STRING, + { .strptr = config_file_options.pg_backupapi_host }, + { .strdefault = "" }, + {}, + { .strmaxlen = sizeof(config_file_options.pg_backupapi_host) }, + {} + }, + /* pg_backupapi_node_name */ + { + "pg_backupapi_node_name", + CONFIG_STRING, + { .strptr = config_file_options.pg_backupapi_node_name }, + { .strdefault = "" }, + {}, + { .strmaxlen = sizeof(config_file_options.pg_backupapi_node_name) }, + {} + }, + /* pg_backupapi_remote_ssh_command */ + { + "pg_backupapi_remote_ssh_command", + CONFIG_STRING, + { .strptr = config_file_options.pg_backupapi_remote_ssh_command }, + { .strdefault = "" }, + {}, + { .strmaxlen = sizeof(config_file_options.pg_backupapi_remote_ssh_command) }, + {} + }, /* ======================= * standby follow settings diff --git a/configfile.h b/configfile.h index 3bcb9f51..3d6f423a 100644 --- a/configfile.h +++ b/configfile.h @@ -164,6 +164,10 @@ typedef struct char archive_cleanup_command[MAXLEN]; bool use_primary_conninfo_password; char passfile[MAXPGPATH]; + char pg_backupapi_backup_id[NAMEDATALEN]; + char pg_backupapi_host[NAMEDATALEN]; + char pg_backupapi_node_name[NAMEDATALEN]; + char pg_backupapi_remote_ssh_command[MAXLEN]; /* standby promote settings */ int promote_check_timeout; diff --git a/errcode.h b/errcode.h index 6bc30c78..0bd91ccd 100644 --- a/errcode.h +++ b/errcode.h @@ -49,5 +49,6 @@ #define ERR_NODE_STATUS 25 #define ERR_REPMGRD_PAUSE 26 #define ERR_REPMGRD_SERVICE 27 +#define ERR_PGBACKUPAPI_SERVICE 28 #endif /* _ERRCODE_H_ */ diff --git a/repmgr-action-standby.c b/repmgr-action-standby.c index cc9829c1..be65de36 100644 --- a/repmgr-action-standby.c +++ b/repmgr-action-standby.c @@ -21,6 +21,7 @@ #include #include +#include #include "repmgr.h" #include "dirutil.h" @@ -29,7 +30,7 @@ #include "repmgr-client-global.h" #include "repmgr-action-standby.h" - +#include "pgbackupapi.h" typedef struct TablespaceDataListCell { @@ -113,6 +114,7 @@ static void check_recovery_type(PGconn *conn); static void initialise_direct_clone(t_node_info *local_node_record, t_node_info *upstream_node_record); static int run_basebackup(t_node_info *node_record); static int run_file_backup(t_node_info *node_record); +static int run_pg_backupapi(t_node_info *node_record); static void copy_configuration_files(bool delete_after_copy); @@ -687,19 +689,18 @@ do_standby_clone(void) exit(SUCCESS); } - if (mode != barman) - { - initialise_direct_clone(&local_node_record, &upstream_node_record); - } - switch (mode) { case pg_basebackup: + initialise_direct_clone(&local_node_record, &upstream_node_record); log_notice(_("starting backup (using pg_basebackup)...")); break; case barman: log_notice(_("retrieving backup from Barman...")); break; + case pg_backupapi: + log_notice(_("starting backup (using pg_backupapi)...")); + break; default: /* should never reach here */ log_error(_("unknown clone mode")); @@ -721,6 +722,9 @@ do_standby_clone(void) case barman: r = run_file_backup(&local_node_record); break; + case pg_backupapi: + r = run_pg_backupapi(&local_node_record); + break; default: /* should never reach here */ log_error(_("unknown clone mode")); @@ -814,7 +818,6 @@ do_standby_clone(void) } /* Write the recovery.conf file */ - if (create_recovery_file(&local_node_record, &recovery_conninfo, source_server_version_num, @@ -846,6 +849,9 @@ do_standby_clone(void) case barman: log_notice(_("standby clone (from Barman) complete")); break; + case pg_backupapi: + log_notice(_("standby clone (from pg_backupapi) complete")); + break; } /* @@ -937,6 +943,9 @@ do_standby_clone(void) case barman: appendPQExpBufferStr(&event_details, "barman"); break; + case pg_backupapi: + appendPQExpBufferStr(&event_details, "pg_backupapi"); + break; } appendPQExpBuffer(&event_details, @@ -7770,6 +7779,86 @@ stop_backup: } +/* + * Perform a call to pg_backupapi endpoint to ask barman to write the backup + * for us. This will ensure that no matter the format on-disk of new backups, + * barman will always find a way how to read and write them. + * From repmgr 4 this is only used for Barman backups. + */ +static int +run_pg_backupapi(t_node_info *local_node_record) +{ + int r = ERR_PGBACKUPAPI_SERVICE; + long http_return_code = 0; + short seconds_to_sleep = 3; + operation_task *task = malloc(sizeof(operation_task)); + CURL *curl = curl_easy_init(); + CURLcode ret; + + + task->host = malloc(strlen(config_file_options.pg_backupapi_host)+1); + task->remote_ssh_command = malloc(strlen(config_file_options.pg_backupapi_remote_ssh_command)+1); + task->node_name = malloc(strlen(config_file_options.pg_backupapi_node_name)+1); + task->operation_type = malloc(strlen(DEFAULT_STANDBY_PG_BACKUPAPI_OP_TYPE)+1); + task->backup_id = malloc(strlen(config_file_options.pg_backupapi_backup_id)+1); + task->destination_directory = malloc(strlen(local_data_directory)+1); + + task->operation_id = malloc(MAX_BUFFER_LENGTH); + task->operation_status = malloc(MAX_BUFFER_LENGTH); + + strcpy(task->host, config_file_options.pg_backupapi_host); + strcpy(task->remote_ssh_command, config_file_options.pg_backupapi_remote_ssh_command); + strcpy(task->node_name, config_file_options.pg_backupapi_node_name); + strcpy(task->operation_type, DEFAULT_STANDBY_PG_BACKUPAPI_OP_TYPE); + strcpy(task->backup_id, config_file_options.pg_backupapi_backup_id); + strcpy(task->destination_directory, local_data_directory); + strcpy(task->operation_id, "\0"); + + ret = create_new_task(curl, task); + + if ((ret != CURLE_OK) || (strlen(task->operation_id) == 0)) { + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_return_code); + if (499 > http_return_code && http_return_code >= 400) { + log_error("Cannot find backup '%s' for node '%s'.", task->backup_id, task->node_name); + } else { + log_error("whilst reaching out pg_backup service: %s\n", curl_easy_strerror(ret)); + } + } + else + { + log_info("Success creating the task: operation id '%s'", task->operation_id); + + //We call init again because previous call included POST calls + curl_easy_cleanup(curl); + curl = curl_easy_init(); + while (true) + { + ret = get_status_of_operation(curl, task); + if (strlen(task->operation_status) == 0) { + log_info("Retrying..."); + } + else + { + log_info("status %s", task->operation_status); + } + if (strcmp(task->operation_status, "FAILED") == 0) { + break; + } + if (strcmp(task->operation_status, "DONE") == 0) { + r = SUCCESS; + break; + } + + sleep(seconds_to_sleep); + } + } + + curl_easy_cleanup(curl); + free(task); + return r; +} + + static char * make_barman_ssh_command(char *buf) { diff --git a/repmgr-client-global.h b/repmgr-client-global.h index c16bb9ea..5ed4f0f2 100644 --- a/repmgr-client-global.h +++ b/repmgr-client-global.h @@ -193,7 +193,8 @@ typedef struct typedef enum { barman, - pg_basebackup + pg_basebackup, + pg_backupapi } standy_clone_mode; typedef enum diff --git a/repmgr-client.c b/repmgr-client.c index ab8ca78f..6fa8edd2 100644 --- a/repmgr-client.c +++ b/repmgr-client.c @@ -3096,9 +3096,14 @@ get_standby_clone_mode(void) if (*config_file_options.barman_host != '\0' && runtime_options.without_barman == false) mode = barman; - else - mode = pg_basebackup; - + else { + if (*config_file_options.pg_backupapi_host != '\0') { + log_info("Attempting to use `pg_backupapi` new restore mode"); + mode = pg_backupapi; + } + else + mode = pg_basebackup; + } return mode; }