Skip to content

Commit

Permalink
Systemd integration (#24)
Browse files Browse the repository at this point in the history
Add the ability for pg_autoctl to produce the systemd unit configuration file for the pgautofailover service, using both the current PGDATA and absolute filename for the current program in use.
  • Loading branch information
DimCitus committed Jun 24, 2019
1 parent 3fd5432 commit d8ba26f
Show file tree
Hide file tree
Showing 16 changed files with 496 additions and 26 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ monitor:
clean-monitor:
$(MAKE) -C src/monitor/ clean

install-monitor:
install-monitor: monitor
$(MAKE) -C src/monitor/ install

check-monitor: install-monitor
Expand All @@ -30,7 +30,7 @@ bin:
clean-bin:
$(MAKE) -C src/bin/ clean

install-bin:
install-bin: bin
$(MAKE) -C src/bin/ install

test:
Expand Down
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ def __init__(self, **options):
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'pg_auto_failover.tex', 'pg_auto_failover Documentation',
(master_doc, 'pg_auto_failover.tex', 'pg\_auto\_failover Documentation',
'Microsoft', 'manual'),
]

Expand Down
38 changes: 38 additions & 0 deletions docs/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,41 @@ Fedora, CentOS, or Red Hat
# confirm installation
/usr/pgsql-11/bin/pg_autoctl --version
Installing a pgautofailover Systemd unit
----------------------------------------

The command ``pg_autoctl show systemd`` outputs a systemd unit file that you
can use to setup a boot-time registered service for pg_auto_failover on your
machine.

Here's a sample output from the command:

.. code-block::
$ pg_autoctl show systemd
13:44:34 INFO HINT: to complete a systemd integration, run the following commands:
13:44:34 INFO pg_autoctl -q show systemd --pgdata /var/lib/postgresql/monitor | sudo tee /etc/systemd/system/pgautofailover.service
13:44:34 INFO sudo systemctl daemon-reload
13:44:34 INFO sudo systemctl start pgautofailover
[Unit]
Description = pg_auto_failover
[Service]
WorkingDirectory = /var/lib/postgresql/monitor
Environment = "PGDATA=/var/lib/postgresql/monitor"
User = postgres
ExecStart = /usr/lib/postgresql/10/bin/pg_autoctl run
Restart = always
StartLimitBurst = 0
[Install]
WantedBy = multi-user.target
Copy/pasting the commands given in the hint output from the command will
enable the pgautofailer service on your system, when using systemd.

It is important that PostgreSQL is started by ``pg_autoctl`` rather than by
systemd itself, as it might be that a failover has been done during a
reboot, for instance, and that once the reboot complete we want the local
Postgres to re-join as a secondary node where it used to be a primary node.
3 changes: 3 additions & 0 deletions src/bin/pg_autoctl/cli_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ extern CommandLine show_uri_command;
extern CommandLine show_events_command;
extern CommandLine show_state_command;

/* cli_systemd.c */
extern CommandLine systemd_cat_service_file_command;


int cli_create_node_getopts(int argc, char **argv,
struct option *long_options,
Expand Down
1 change: 1 addition & 0 deletions src/bin/pg_autoctl/cli_root.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ CommandLine *show_subcommands[] = {
&show_uri_command,
&show_events_command,
&show_state_command,
&systemd_cat_service_file_command,
NULL
};

Expand Down
2 changes: 2 additions & 0 deletions src/bin/pg_autoctl/cli_root.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

#include "commandline.h"

extern char pg_autoctl_argv0[];

extern CommandLine help;
extern CommandLine version;

Expand Down
127 changes: 127 additions & 0 deletions src/bin/pg_autoctl/cli_systemd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* src/bin/pg_autoctl/cli_do_systemd.c
* Implementation of a CLI which lets you run operations on the local
* postgres server directly.
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the PostgreSQL License.
*
*/

#include <errno.h>
#include <getopt.h>
#include <inttypes.h>
#include <signal.h>

#include "postgres_fe.h"

#include "cli_common.h"
#include "cli_do_root.h"
#include "commandline.h"
#include "config.h"
#include "defaults.h"
#include "file_utils.h"
#include "keeper_config.h"
#include "keeper.h"
#include "systemd_config.h"

static SystemdServiceConfig systemdOptions;


static int cli_systemd_getopt(int argc, char **argv);
static void cli_systemd_enable_service(int argc, char **argv);
static void cli_systemd_cat_service_file(int argc, char **argv);

/* pg_autoctl show systemd, see cli_show.c */
CommandLine systemd_cat_service_file_command =
make_command("systemd",
"Print systemd service file for this node", "", "",
cli_systemd_getopt, cli_systemd_cat_service_file);


/*
* cli_systemd_getopt parses the command line options necessary to handle
* systemd integration for the pg_autoctl keeper service.
*/
int
cli_systemd_getopt(int argc, char **argv)
{
SystemdServiceConfig options = { 0 };

int c = 0, option_index = 0, errors = 0;

static struct option long_options[] = {
{ "pgdata", required_argument, NULL, 'D' },
{ "help", no_argument, NULL, 'h' },
{ NULL, 0, NULL, 0 }
};

optind = 0;

while ((c = getopt_long(argc, argv, "D:h",
long_options, &option_index)) != -1)
{
switch (c)
{
case 'D':
{
strlcpy(options.pgSetup.pgdata, optarg, MAXPGPATH);
log_trace("--pgdata %s", options.pgSetup.pgdata);
break;
}

}
}

if (IS_EMPTY_STRING_BUFFER(options.pgSetup.pgdata))
{
char *pgdata = getenv("PGDATA");

if (pgdata == NULL)
{
log_fatal("Failed to set PGDATA either from the environment "
"or from --pgdata");
exit(EXIT_CODE_BAD_ARGS);
}

strlcpy(options.pgSetup.pgdata, pgdata, MAXPGPATH);
}

if (!keeper_config_set_pathnames_from_pgdata(&options.pathnames,
options.pgSetup.pgdata))
{
/* errors have already been logged */
exit(EXIT_CODE_BAD_CONFIG);
}

/* publish our option parsing in the global variable */
systemdOptions = options;

return optind;
}


/*
* cli_systemd_cat_service_file prints the systemd service file for this
* pg_autoctl node.
*/
static void
cli_systemd_cat_service_file(int argc, char **argv)
{
SystemdServiceConfig config = systemdOptions;
PostgresSetup pgSetup = { 0 };

(void) systemd_config_init(&config, pgSetup.pgdata);

log_info("HINT: to complete a systemd integration, "
"run the following commands:");
log_info("pg_autoctl -q show systemd --pgdata \"%s\" | sudo tee %s",
config.pgSetup.pgdata, config.pathnames.systemd);
log_info("sudo systemctl daemon-reload");
log_info("sudo systemctl start pgautofailover");

if (!systemd_config_write(stdout, &config))
{
exit(EXIT_CODE_INTERNAL_ERROR);
}
}
61 changes: 39 additions & 22 deletions src/bin/pg_autoctl/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#include "pgctl.h"



/*
* build_xdg_path is an helper function that builds the full path to an XDG
* compatible resource: either a configuration file, a runtime file, or a data
Expand Down Expand Up @@ -154,28 +153,10 @@ build_xdg_path(char *dst,
join_path_components(filename, filename, name);

/* normalize the path to the configuration file, if it exists */
if (file_exists(filename))
{
char realPath[PATH_MAX];

if (realpath(filename, realPath) == NULL)
{
log_fatal("Failed to normalize file name \"%s\": %s",
filename, strerror(errno));
return false;
}

if (strlcpy(dst, realPath, MAXPGPATH) >= MAXPGPATH)
{
log_fatal("Real path \"%s\" is %d bytes long, and pg_autoctl "
"is limited to handling paths of %d bytes long, maximum",
realPath, (int) strlen(realPath), MAXPGPATH);
return false;
}
}
else
if (!normalize_filename(filename, dst, MAXPGPATH))
{
strlcpy(dst, filename, MAXPGPATH);
/* errors have already been logged */
return false;
}

return true;
Expand Down Expand Up @@ -325,3 +306,39 @@ ProbeConfigurationFileRole(const char *filename)
/* can't happen: keep compiler happy */
return PG_AUTOCTL_ROLE_UNKNOWN;
}


/*
* normalize_filename returns the real path of a given filename, resolving
* symlinks and pruning double-slashes and other weird constructs.
*/
bool
normalize_filename(const char *filename, char *dst, int size)
{
/* normalize the path to the configuration file, if it exists */
if (file_exists(filename))
{
char realPath[PATH_MAX];

if (realpath(filename, realPath) == NULL)
{
log_fatal("Failed to normalize file name \"%s\": %s",
filename, strerror(errno));
return false;
}

if (strlcpy(dst, realPath, size) >= size)
{
log_fatal("Real path \"%s\" is %d bytes long, and pg_autoctl "
"is limited to handling paths of %d bytes long, maximum",
realPath, (int) strlen(realPath), size);
return false;
}
}
else
{
strlcpy(dst, filename, MAXPGPATH);
}

return true;
}
2 changes: 2 additions & 0 deletions src/bin/pg_autoctl/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ typedef struct ConfigFilePaths
char state[MAXPGPATH]; /* ~/.local/share/pg_autoctl/${PGDATA}/pg_autoctl.state */
char pid[MAXPGPATH]; /* /tmp/${PGDATA}/pg_autoctl.pid */
char init[MAXPGPATH]; /* /tmp/${PGDATA}/pg_autoctl.init */
char systemd[MAXPGPATH]; /* ~/.config/systemd/user/pgautofailover.service */
} ConfigFilePaths;

/*
Expand Down Expand Up @@ -65,6 +66,7 @@ typedef enum

bool build_xdg_path(char *dst, XDGResourceType xdgType,
const char *pgdata, const char *name);
bool normalize_filename(const char *filename, char *dst, int size);

bool SetConfigFilePath(ConfigFilePaths *pathnames, const char *pgdata);
bool SetStateFilePath(ConfigFilePaths *pathnames, const char *pgdata);
Expand Down
3 changes: 3 additions & 0 deletions src/bin/pg_autoctl/defaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
#define KEEPER_PID_FILENAME "pg_autoctl.pid"
#define KEEPER_INIT_FILENAME "pg_autoctl.init"

#define KEEPER_SYSTEMD_SERVICE "pgautofailover"
#define KEEPER_SYSTEMD_FILENAME "pgautofailover.service"

/* pg_auto_failover monitor related constants */
#define PG_AUTOCTL_HEALTH_USERNAME "pgautofailover_monitor"
#define PG_AUTOCTL_REPLICA_USERNAME "pgautofailover_replicator"
Expand Down

0 comments on commit d8ba26f

Please sign in to comment.