mirror of
https://github.com/EnterpriseDB/repmgr.git
synced 2026-03-23 07:06:30 +00:00
Compare commits
31 Commits
test-UPM-1
...
dev/update
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd60929910 | ||
|
|
82ff0d6873 | ||
|
|
26f9cb3f95 | ||
|
|
890cbd9fca | ||
|
|
440ba5fbb4 | ||
|
|
20f39e2fbe | ||
|
|
afdeb5d7b2 | ||
|
|
7d45ab04b3 | ||
|
|
7af74c4fda | ||
|
|
1010aeb3dd | ||
|
|
c58c95c9f1 | ||
|
|
3887637f1e | ||
|
|
7a84e34630 | ||
|
|
9ab4acea78 | ||
|
|
ef962436f7 | ||
|
|
9a68206b4a | ||
|
|
31af938354 | ||
|
|
061025b62f | ||
|
|
02d8e0c808 | ||
|
|
167d166ae8 | ||
|
|
03c2ae1bd8 | ||
|
|
4021037d38 | ||
|
|
7cd7566409 | ||
|
|
81c3200ef2 | ||
|
|
c6366db6f9 | ||
|
|
078a47e182 | ||
|
|
ed2c0aaf0b | ||
|
|
6249027a03 | ||
|
|
b36fca17d9 | ||
|
|
a3b919d599 | ||
|
|
c21d452076 |
37
.github/workflows/sonarqube-scan.yml
vendored
37
.github/workflows/sonarqube-scan.yml
vendored
@@ -1,37 +0,0 @@
|
||||
name: SonarQube Scan
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [ master ]
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
sonarQube:
|
||||
name: SonarQube-Job
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
- name: Checkout source repo
|
||||
uses: actions/checkout@v1
|
||||
with:
|
||||
ref: '${{ github.head_ref }}'
|
||||
|
||||
- name: Checkout GitHub Action Repo
|
||||
uses: actions/checkout@master
|
||||
with:
|
||||
repository: EnterpriseDB/edb-github-actions.git
|
||||
ref: master
|
||||
token: ${{ secrets.GH_SLONIK }}
|
||||
path: .github/actions/edb-github-actions
|
||||
|
||||
- name: SonarQube Scan
|
||||
uses: ./.github/actions/edb-github-actions/sonarqube
|
||||
with:
|
||||
REPO_NAME: '${{github.event.repository.name}}'
|
||||
SONAR_PROJECT_KEY: EnterpriseDB_repmgr
|
||||
SONAR_URL: '${{secrets.SONARQUBE_URL}}'
|
||||
SONAR_LOGIN: '${{secrets.SONARQUBE_LOGIN}}'
|
||||
PULL_REQUEST_KEY: '${{github.event.number}}'
|
||||
PULL_REQUEST_BRANCH: '${{github.head_ref}}'
|
||||
PULL_REQUEST_BASE_BRANCH: '${{github.base_ref}}'
|
||||
REPO_DEFAULT_BRANCH: '${{github.event.repository.default_branch}}'
|
||||
REPO_EXCLUDE_FILES: '*properties*,**/src/test/**/*,**/*.sql,**/docs/**/*,**/*/*.java'
|
||||
18
.github/workflows/sonarqube/configure-env.sh
vendored
18
.github/workflows/sonarqube/configure-env.sh
vendored
@@ -1,18 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt-get -y install debhelper curl autoconf zlib1g-dev \
|
||||
libedit-dev libxml2-dev libxslt1-dev libkrb5-dev libssl-dev libpam0g-dev systemtap-sdt-dev \
|
||||
libselinux1-dev build-essential bison apt-utils lsb-release devscripts \
|
||||
software-properties-common git shellcheck flex
|
||||
|
||||
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
||||
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
sudo apt-get update
|
||||
|
||||
sudo apt-get -y install libpq-dev postgresql-13 postgresql-server-dev-13
|
||||
|
||||
./configure
|
||||
|
||||
export PG_CONFIG=/usr/bin/pg_config
|
||||
|
||||
/home/buildfarm/sonar/depends/build-wrapper-linux-x86/build-wrapper-linux-x86-64 --out-dir build_wrapper_output_directory make
|
||||
6
HISTORY
6
HISTORY
@@ -1,3 +1,9 @@
|
||||
5.4.1 2023-07-04
|
||||
repmgrd: ensure witness node metadata is updated (Ian)
|
||||
|
||||
5.4.0 2023-03-16
|
||||
Support cloning replicas using pg-backup-api
|
||||
|
||||
5.3.3 2022-10-17
|
||||
Support for PostgreSQL added
|
||||
repmgrd: ensure event notification script is called for event
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ replication, and perform administrative tasks such as failover or switchover
|
||||
operations.
|
||||
|
||||
The most recent `repmgr` version (5.3.2) supports all PostgreSQL versions from
|
||||
9.5 to 14. PostgreSQL 9.4 is also supported, with some restrictions.
|
||||
9.5 to 15. PostgreSQL 9.4 is also supported, with some restrictions.
|
||||
|
||||
`repmgr` is distributed under the GNU GPL 3 and maintained by EnterpriseDB.
|
||||
|
||||
@@ -40,7 +40,6 @@ Directories
|
||||
- `contrib/`: additional utilities
|
||||
- `doc/`: DocBook-based documentation files
|
||||
- `expected/`: expected regression test output
|
||||
- `scripts/`: example scripts
|
||||
- `sql/`: regression test input
|
||||
|
||||
|
||||
@@ -70,7 +69,6 @@ news are always welcome.
|
||||
|
||||
Thanks from the repmgr core team.
|
||||
|
||||
* Ian Barwick
|
||||
* Jaime Casanova
|
||||
* Abhijit Menon-Sen
|
||||
* Simon Riggs
|
||||
|
||||
40
configdata.c
40
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
24
configure.in
24
configure.in
@@ -1,8 +1,8 @@
|
||||
AC_INIT([repmgr], [5.4.0], [repmgr@googlegroups.com], [repmgr], [https://repmgr.org/])
|
||||
AC_INIT([repmgr],[5.4.1],[repmgr@googlegroups.com],[repmgr],[https://www.repmgr.org/])
|
||||
|
||||
AC_COPYRIGHT([Copyright (c) 2010-2021, EnterpriseDB Corporation])
|
||||
|
||||
AC_CONFIG_HEADER(config.h)
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
AC_ARG_VAR([PG_CONFIG], [Location to find pg_config for target PostgreSQL (default PATH)])
|
||||
|
||||
@@ -60,6 +60,7 @@ AC_SUBST(vpath_build)
|
||||
AC_CHECK_PROG(HAVE_GNUSED,gnused,yes,no)
|
||||
AC_CHECK_PROG(HAVE_GSED,gsed,yes,no)
|
||||
AC_CHECK_PROG(HAVE_SED,sed,yes,no)
|
||||
AC_CHECK_PROG(HAVE_FLEX,flex,yes,no)
|
||||
|
||||
if test "$HAVE_GNUSED" = yes; then
|
||||
SED=gnused
|
||||
@@ -72,6 +73,25 @@ else
|
||||
fi
|
||||
AC_SUBST(SED)
|
||||
|
||||
AS_IF([test x"$HAVE_FLEX" != x"yes"], AC_MSG_ERROR([flex should be installed first]))
|
||||
|
||||
#Checking libraries
|
||||
GENERIC_LIB_FAILED_MSG="library should be installed"
|
||||
|
||||
AC_CHECK_LIB(selinux, is_selinux_enabled, [],
|
||||
[AC_MSG_ERROR(['selinux' $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CHECK_LIB(lz4, LZ4_compress_default, [],
|
||||
[AC_MSG_ERROR(['Z4' $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CHECK_LIB(xslt, xsltCleanupGlobals, [],
|
||||
[AC_MSG_ERROR(['xslt' $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CHECK_LIB(pam, pam_start, [],
|
||||
[AC_MSG_ERROR(['pam' $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CHECK_LIB(gssapi_krb5, gss_init_sec_context, [],
|
||||
[AC_MSG_ERROR([gssapi_krb5 $GENERIC_LIB_FAILED_MSG])])
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_CONFIG_FILES([Makefile.global])
|
||||
|
||||
@@ -16,8 +16,44 @@
|
||||
</para>
|
||||
|
||||
<!-- remember to update the release date in ../repmgr_version.h.in -->
|
||||
<sect1 id="release-5.4.1">
|
||||
<title id="release-current">Release 5.4.1</title>
|
||||
<para><emphasis>Tue 04 July, 2023</emphasis></para>
|
||||
<para>
|
||||
&repmgr; 5.4.1 is a minor release providing a fix for witness metadata update
|
||||
</para>
|
||||
<sect2>
|
||||
<title>Bug fixes</title>
|
||||
<para>
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>
|
||||
&repmgrd;: ensure witness node metadata is updated if the primary
|
||||
node changed while the witness &repmgrd; was not running.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
</para>
|
||||
</sect2>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="release-5.4.0">
|
||||
<title>Release 5.4.0</title>
|
||||
<para><emphasis>Thu 15 March, 2023</emphasis></para>
|
||||
<para>
|
||||
&repmgr; 5.4.0 is a major release.
|
||||
</para>
|
||||
<para>
|
||||
This release provides support for cloning standbys using backups taken with <ulink url="http://www.pgbarman.org">barman</ulink>
|
||||
with the use of <ulink url="https://github.com/EnterpriseDB/pg-backup-api">pg-backup-api</ulink>.
|
||||
</para>
|
||||
<para>
|
||||
Minor fixes to the documentation.
|
||||
</para>
|
||||
</sect1>
|
||||
|
||||
<sect1 id="release-5.3.3">
|
||||
<title id="release-current">Release 5.3.3</title>
|
||||
<title>Release 5.3.3</title>
|
||||
<para><emphasis>Mon 17 October, 2022</emphasis></para>
|
||||
<para>
|
||||
&repmgr; 5.3.3 is a minor release providing support for
|
||||
|
||||
@@ -225,6 +225,109 @@ description = "Main cluster"
|
||||
</note>
|
||||
|
||||
</sect2>
|
||||
|
||||
<sect2 id="cloning-from-barman-pg_backupapi-mode" xreflabel="Using Barman through its API (pg-backup-api)">
|
||||
<title>Using Barman through its API (pg-backup-api)</title>
|
||||
<indexterm>
|
||||
<primary>cloning</primary>
|
||||
<secondary>pg-backup-api</secondary>
|
||||
</indexterm>
|
||||
|
||||
<para>
|
||||
You can find information on how to install and setup pg-backup-api in
|
||||
<ulink url="https://www.enterprisedb.com/docs/supported-open-source/barman/pg-backup-api/">the pg-backup-api
|
||||
documentation</ulink>.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
This mode (`pg-backupapi`) was introduced in v5.4.0 as a way to further integrate with Barman letting Barman
|
||||
handle the restore. This also reduces the ssh keys that need to share between the backup and postgres nodes.
|
||||
As long as you have access to the API service by HTTP calls, you could perform recoveries right away.
|
||||
You just need to instruct Barman through the API which backup you need and on which node the backup needs to
|
||||
to be restored on.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
In order to enable <literal>pg_backupapi mode</literal> support for <command>repmgr standby clone</command>,
|
||||
you need the following lines in repmgr.conf:
|
||||
<itemizedlist spacing="compact" mark="bullet">
|
||||
<listitem><para>pg_backupapi_host: Where pg-backup-api is hosted</para></listitem>
|
||||
<listitem><para>pg_backupapi_node_name: Name of the server as understood by Barman</para></listitem>
|
||||
<listitem><para>pg_backupapi_remote_ssh_command: How Barman will be connecting as to the node</para></listitem>
|
||||
<listitem><para>pg_backupapi_backup_id: ID of the existing backup you need to restore</para></listitem>
|
||||
</itemizedlist>
|
||||
|
||||
This is an example of how repmgr.conf would look like:
|
||||
|
||||
<programlisting>
|
||||
pg_backupapi_host = '192.168.122.154'
|
||||
pg_backupapi_node_name = 'burrito'
|
||||
pg_backupapi_remote_ssh_command = 'ssh john_doe@192.168.122.1'
|
||||
pg_backupapi_backup_id = '20230223T093201'
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<literal>pg_backupapi_host</literal> is the variable name that enables this mode, and when you set it,
|
||||
all the rest of the above variables are required. Also, remember that this service is just an interface
|
||||
between Barman and repmgr, hence if something fails during a recovery, you should check Barman's logs upon
|
||||
why the process couldn't finish properly.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<simpara>
|
||||
Despite in Barman you can define shortcuts like "lastest" or "oldest", they are not supported for the
|
||||
time being in pg-backup-api. These shortcuts will be supported in a future release.
|
||||
</simpara>
|
||||
</note>
|
||||
|
||||
<para>
|
||||
This is a real example of repmgr's output cloning with the API. Note that during this operation, we stopped
|
||||
the service for a little while and repmgr had to retry but that doesn't affect the final outcome. The primary
|
||||
is listening on localhost's port 6001:
|
||||
|
||||
<programlisting>
|
||||
$ repmgr -f ~/nodes/node_3/repmgr.conf standby clone -U repmgr -p 6001 -h localhost
|
||||
NOTICE: destination directory "/home/mario/nodes/node_3/data" provided
|
||||
INFO: Attempting to use `pg_backupapi` new restore mode
|
||||
INFO: connecting to source node
|
||||
DETAIL: connection string is: user=repmgr port=6001 host=localhost
|
||||
DETAIL: current installation size is 8541 MB
|
||||
DEBUG: 1 node records returned by source node
|
||||
DEBUG: connecting to: "user=repmgr dbname=repmgr host=localhost port=6001 connect_timeout=2 fallback_application_name=repmgr options=-csearch_path="
|
||||
DEBUG: upstream_node_id determined as 1
|
||||
INFO: Attempting to use `pg_backupapi` new restore mode
|
||||
INFO: replication slot usage not requested; no replication slot will be set up for this standby
|
||||
NOTICE: starting backup (using pg_backupapi)...
|
||||
INFO: Success creating the task: operation id '20230309T150647'
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
Incorrect reply received for that operation ID.
|
||||
INFO: Retrying...
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status IN_PROGRESS
|
||||
INFO: status DONE
|
||||
NOTICE: standby clone (from pg_backupapi) complete
|
||||
NOTICE: you can now start your PostgreSQL server
|
||||
HINT: for example: pg_ctl -D /home/mario/nodes/node_3/data start
|
||||
HINT: after starting the server, you need to register this standby with "repmgr standby register"
|
||||
</programlisting>
|
||||
|
||||
</para>
|
||||
</sect2> <!--END cloning-from-barman-pg_backupapi-mode !-->
|
||||
</sect1>
|
||||
|
||||
<sect1 id="cloning-replication-slots" xreflabel="Cloning and replication slots">
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
<note>
|
||||
<simpara>
|
||||
This section documents a subset of optional configuration settings; for a full
|
||||
for a full and annotated view of all configuration options see the
|
||||
see the <ulink url="https://raw.githubusercontent.com/EnterpriseDB/repmgr/master/repmgr.conf.sample">sample repmgr.conf file</ulink>
|
||||
and annotated view of all configuration options see the
|
||||
<ulink url="https://raw.githubusercontent.com/EnterpriseDB/repmgr/master/repmgr.conf.sample">sample repmgr.conf file</ulink>
|
||||
</simpara>
|
||||
</note>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
</para>
|
||||
|
||||
<para>
|
||||
&repmgr; &repmgrversion; is compatible with all PostgreSQL versions from 9.4. See
|
||||
&repmgr; &repmgrversion; is compatible with all PostgreSQL versions from 10. See
|
||||
section <link linkend="install-compatibility-matrix">&repmgr; compatibility matrix</link>
|
||||
for an overview of version compatibility.
|
||||
</para>
|
||||
@@ -120,13 +120,13 @@
|
||||
&repmgr; 5.4
|
||||
</entry>
|
||||
<entry>
|
||||
(dev)
|
||||
YES
|
||||
</entry>
|
||||
<entry>
|
||||
<link linkend="release-current">&repmgrversion;</link> (&releasedate;)
|
||||
</entry>
|
||||
<entry>
|
||||
9.4, 9.5, 9.6, 10, 11, 12, 13, 15
|
||||
10, 11, 12, 13, 14, 15, 16
|
||||
</entry>
|
||||
<entry>
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<abstract>
|
||||
<para>
|
||||
This is the official documentation of &repmgr; &repmgrversion; for
|
||||
use with PostgreSQL 9.4 - PostgreSQL 15.
|
||||
use with PostgreSQL 10 - PostgreSQL 16.
|
||||
</para>
|
||||
<para>
|
||||
&repmgr; is being continually developed and we strongly recommend using the
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
<listitem>
|
||||
<simpara>
|
||||
option to execute custom scripts ("<link linkend="event-notifications">event notifications</link>
|
||||
option to execute custom scripts ("<link linkend="event-notifications">event notifications</link>")
|
||||
at different points in the failover sequence
|
||||
</simpara>
|
||||
</listitem>
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
147
pgbackupapi.c
Normal file
147
pgbackupapi.c
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* pgbackupapi.c
|
||||
* Copyright (c) EnterpriseDB Corporation, 2010-2021
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
#include "repmgr.h"
|
||||
#include "pgbackupapi.h"
|
||||
|
||||
|
||||
size_t receive_operations_cb(void *content, size_t size, size_t nmemb, char *buffer) {
|
||||
short int max_chars_to_copy = MAX_BUFFER_LENGTH -2;
|
||||
short int i = 0;
|
||||
int operation_length = 0;
|
||||
json_object *value;
|
||||
|
||||
json_object *root = json_tokener_parse(content);
|
||||
json_object *operations = json_object_object_get(root, "operations");
|
||||
|
||||
operation_length = strlen(json_object_get_string(operations));
|
||||
if (operation_length < max_chars_to_copy) {
|
||||
max_chars_to_copy = operation_length;
|
||||
}
|
||||
|
||||
strncpy(buffer, json_object_get_string(operations), max_chars_to_copy);
|
||||
|
||||
fprintf(stdout, "Success! The following operations were found\n");
|
||||
for (i=0; i<json_object_array_length(operations); i++) {
|
||||
value = json_object_array_get_idx(operations, i);
|
||||
printf("%s\n", json_object_get_string(value));
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
char * define_base_url(operation_task *task) {
|
||||
char *format = "http://%s:7480/servers/%s/operations";
|
||||
char *url = malloc(MAX_BUFFER_LENGTH);
|
||||
|
||||
snprintf(url, MAX_BUFFER_LENGTH-1, format, task->host, task->node_name);
|
||||
|
||||
//`url` is freed on the function that called this
|
||||
return url;
|
||||
}
|
||||
|
||||
CURLcode get_operations_on_server(CURL *curl, operation_task *task) {
|
||||
char buffer[MAX_BUFFER_LENGTH];
|
||||
char *url = define_base_url(task);
|
||||
CURLcode ret;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_operations_cb);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
ret = curl_easy_perform(curl);
|
||||
free(url);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t receive_operation_id(void *content, size_t size, size_t nmemb, char *buffer) {
|
||||
json_object *root = json_tokener_parse(content);
|
||||
json_object *operation = json_object_object_get(root, "operation_id");
|
||||
|
||||
if (operation != NULL) {
|
||||
strncpy(buffer, json_object_get_string(operation), MAX_BUFFER_LENGTH-2);
|
||||
}
|
||||
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
|
||||
CURLcode create_new_task(CURL *curl, operation_task *task) {
|
||||
PQExpBufferData payload;
|
||||
char *url = define_base_url(task);
|
||||
CURLcode ret;
|
||||
json_object *root = json_object_new_object();
|
||||
struct curl_slist *chunk = NULL;
|
||||
|
||||
json_object_object_add(root, "operation_type", json_object_new_string(task->operation_type));
|
||||
json_object_object_add(root, "backup_id", json_object_new_string(task->backup_id));
|
||||
json_object_object_add(root, "remote_ssh_command", json_object_new_string(task->remote_ssh_command));
|
||||
json_object_object_add(root, "destination_directory", json_object_new_string(task->destination_directory));
|
||||
|
||||
initPQExpBuffer(&payload);
|
||||
appendPQExpBufferStr(&payload, json_object_to_json_string(root));
|
||||
|
||||
chunk = curl_slist_append(chunk, "Content-type: application/json");
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.data);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
//curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_operation_id);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, task->operation_id);
|
||||
ret = curl_easy_perform(curl);
|
||||
free(url);
|
||||
termPQExpBuffer(&payload);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
size_t receive_operation_status(void *content, size_t size, size_t nmemb, char *buffer) {
|
||||
json_object *root = json_tokener_parse(content);
|
||||
json_object *status = json_object_object_get(root, "status");
|
||||
if (status != NULL) {
|
||||
strncpy(buffer, json_object_get_string(status), MAX_BUFFER_LENGTH-2);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Incorrect reply received for that operation ID.\n");
|
||||
strcpy(buffer, "\0");
|
||||
}
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
CURLcode get_status_of_operation(CURL *curl, operation_task *task) {
|
||||
CURLcode ret;
|
||||
char *url = define_base_url(task);
|
||||
|
||||
strcat(url, "/");
|
||||
strcat(url, task->operation_id);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, receive_operation_status);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, task->operation_status);
|
||||
|
||||
ret = curl_easy_perform(curl);
|
||||
free(url);
|
||||
|
||||
return ret;
|
||||
}
|
||||
46
pgbackupapi.h
Normal file
46
pgbackupapi.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* pgbackupapi.h
|
||||
* Copyright (c) EnterpriseDB Corporation, 2010-2021
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <curl/curl.h>
|
||||
#include <json-c/json.h>
|
||||
|
||||
typedef struct operation_task {
|
||||
char *backup_id;
|
||||
char *destination_directory;
|
||||
char *operation_type;
|
||||
char *operation_id;
|
||||
char *operation_status;
|
||||
char *remote_ssh_command;
|
||||
char *host;
|
||||
char *node_name;
|
||||
} operation_task;
|
||||
|
||||
//Default simplebuffer size in most of operations
|
||||
#define MAX_BUFFER_LENGTH 72
|
||||
|
||||
//Callbacks to send/receive data from pg-backup-api endpoints
|
||||
size_t receive_operations_cb(void *content, size_t size, size_t nmemb, char *buffer);
|
||||
size_t receive_operation_id(void *content, size_t size, size_t nmemb, char *buffer);
|
||||
size_t receive_operation_status(void *content, size_t size, size_t nmemb, char *buffer);
|
||||
|
||||
//Functions that implement the logic and know what to do and how to comunnicate wuth the API
|
||||
CURLcode get_operations_on_server(CURL *curl, operation_task *task);
|
||||
CURLcode create_new_task(CURL *curl, operation_task *task);
|
||||
CURLcode get_status_of_operation(CURL *curl, operation_task *task);
|
||||
|
||||
//Helper to make simpler to read the handler where we set the URL
|
||||
char * define_base_url(operation_task *task);
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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,117 @@ 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;
|
||||
|
||||
check_pg_backupapi_standby_clone_options();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_backupapi mode is enabled when config_file_options.pg_backupapi_host is set hence, we
|
||||
* should also check the other required variables too.
|
||||
*/
|
||||
|
||||
void check_pg_backupapi_standby_clone_options() {
|
||||
|
||||
bool error = false;
|
||||
|
||||
if (*config_file_options.pg_backupapi_remote_ssh_command == '\0') {
|
||||
log_hint("Check config: remote ssh command is required");
|
||||
error = true;
|
||||
}
|
||||
if (*config_file_options.pg_backupapi_node_name == '\0') {
|
||||
log_hint("Check config: node name is required");
|
||||
error = true;
|
||||
}
|
||||
if (*config_file_options.pg_backupapi_backup_id == '\0') {
|
||||
log_hint("Check config: backup_id is required");
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error == true) {
|
||||
log_error("Please fix the errors and try again");
|
||||
exit(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
static char *
|
||||
make_barman_ssh_command(char *buf)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,6 @@ extern void do_standby_help(void);
|
||||
|
||||
extern bool do_standby_follow_internal(PGconn *primary_conn, PGconn *follow_target_conn, t_node_info *follow_target_node_record, PQExpBufferData *output, int general_error_code, int *error_code);
|
||||
|
||||
|
||||
void check_pg_backupapi_standby_clone_options(void);
|
||||
|
||||
#endif /* _REPMGR_ACTION_STANDBY_H_ */
|
||||
|
||||
@@ -193,7 +193,8 @@ typedef struct
|
||||
typedef enum
|
||||
{
|
||||
barman,
|
||||
pg_basebackup
|
||||
pg_basebackup,
|
||||
pg_backupapi
|
||||
} standy_clone_mode;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
1
repmgr.h
1
repmgr.h
@@ -116,6 +116,7 @@
|
||||
#define DEFAULT_STANDBY_FOLLOW_TIMEOUT 30 /* seconds */
|
||||
#define DEFAULT_STANDBY_FOLLOW_RESTART false
|
||||
#define DEFAULT_SHUTDOWN_CHECK_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_STANDBY_PG_BACKUPAPI_OP_TYPE "recovery"
|
||||
#define DEFAULT_STANDBY_RECONNECT_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_NODE_REJOIN_TIMEOUT 60 /* seconds */
|
||||
#define DEFAULT_ARCHIVE_READY_WARNING 16 /* WAL files */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#define REPMGR_VERSION_DATE ""
|
||||
#define REPMGR_VERSION "5.4dev"
|
||||
#define REPMGR_VERSION_NUM 50400
|
||||
#define REPMGR_VERSION "5.4.1"
|
||||
#define REPMGR_VERSION_NUM 50401
|
||||
#define REPMGR_EXTENSION_VERSION "5.4"
|
||||
#define REPMGR_EXTENSION_NUM 50400
|
||||
#define REPMGR_RELEASE_DATE "2022-XX-XX"
|
||||
#define REPMGR_RELEASE_DATE "2023-07-04"
|
||||
#define PG_ACTUAL_VERSION_NUM
|
||||
|
||||
@@ -2394,6 +2394,17 @@ monitor_streaming_witness(void)
|
||||
terminate(ERR_BAD_CONFIG);
|
||||
}
|
||||
|
||||
/*
|
||||
* It's possible that the primary changed while the witness repmgrd was not
|
||||
* running. This does not affect the functionality of the witness repmgrd, but
|
||||
* does mean outdated node metadata will be displayed, so update that.
|
||||
*/
|
||||
if (local_node_info.upstream_node_id != primary_node_id)
|
||||
{
|
||||
update_node_record_set_upstream(primary_conn, local_node_info.node_id, primary_node_id);
|
||||
local_node_info.upstream_node_id = primary_node_id;
|
||||
}
|
||||
|
||||
initPQExpBuffer(&event_details);
|
||||
|
||||
appendPQExpBuffer(&event_details,
|
||||
|
||||
Reference in New Issue
Block a user