active_postgres 0.9.1 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/active_postgres/components/core.rb +5 -5
- data/lib/active_postgres/components/repmgr.rb +81 -2
- data/lib/active_postgres/secrets.rb +18 -0
- data/lib/active_postgres/ssh_executor.rb +1 -0
- data/lib/active_postgres/version.rb +1 -1
- data/templates/repmgr.conf.erb +7 -0
- data/templates/repmgr_dns_failover.sh.erb +9 -5
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e51d5f1214cedf497a9b060e5c667c43711f9ff5e36024bfd8dc926b50fe272f
|
|
4
|
+
data.tar.gz: 618fa70d39a43e1c74ba2ed449b75138670bca92cb6bf418606f772de34c6a7d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 254b6fb4342d4e510076d05ebfbeaa0e7990b463df13ad6ddb7d6eb5a70562c23cc4f502ce6c435c6372fc3dd5f695285ee3fdfed522cc188f1b1cc21c6cc90d
|
|
7
|
+
data.tar.gz: d9147a4679529344ccb0ffd3d5c2ffacc3072464b9cf4e1c6e11150a66c142ef0d0b45f9b9cd9d34eceba6f4d0e50baa7c2f227a5b2b1719502972e2b4b39ee4
|
|
@@ -49,6 +49,11 @@ module ActivePostgres
|
|
|
49
49
|
create_app_user_and_database(config.primary_host)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
def install_packages_only(host)
|
|
53
|
+
puts " Installing packages on #{host} (cluster will be created by repmgr)..."
|
|
54
|
+
ssh_executor.install_postgres(host, config.version)
|
|
55
|
+
end
|
|
56
|
+
|
|
52
57
|
private
|
|
53
58
|
|
|
54
59
|
def install_on_host(host, is_primary:)
|
|
@@ -96,11 +101,6 @@ module ActivePostgres
|
|
|
96
101
|
optimal_settings.merge(user_postgresql)
|
|
97
102
|
end
|
|
98
103
|
|
|
99
|
-
def install_packages_only(host)
|
|
100
|
-
puts " Installing packages on #{host} (cluster will be created by repmgr)..."
|
|
101
|
-
ssh_executor.install_postgres(host, config.version)
|
|
102
|
-
end
|
|
103
|
-
|
|
104
104
|
def cluster_exists?(host)
|
|
105
105
|
exists = false
|
|
106
106
|
version = config.version
|
|
@@ -85,6 +85,22 @@ module ActivePostgres
|
|
|
85
85
|
execute :sudo, 'DEBIAN_FRONTEND=noninteractive', 'apt-get', 'install', '-y', '-qq',
|
|
86
86
|
"postgresql-#{version}-repmgr"
|
|
87
87
|
end
|
|
88
|
+
install_postgres_sudoers(host)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def install_postgres_sudoers(host)
|
|
93
|
+
version = config.version
|
|
94
|
+
sudoers_line = "postgres ALL=(ALL) NOPASSWD: /usr/bin/systemctl start postgresql@#{version}-main, " \
|
|
95
|
+
"/usr/bin/systemctl stop postgresql@#{version}-main, " \
|
|
96
|
+
"/usr/bin/systemctl restart postgresql@#{version}-main, " \
|
|
97
|
+
"/usr/bin/systemctl reload postgresql@#{version}-main, " \
|
|
98
|
+
"/usr/bin/systemctl status postgresql@#{version}-main"
|
|
99
|
+
ssh_executor.execute_on_host(host) do
|
|
100
|
+
upload! StringIO.new("#{sudoers_line}\n"), '/tmp/postgres-repmgr-sudoers'
|
|
101
|
+
execute :sudo, 'cp', '/tmp/postgres-repmgr-sudoers', '/etc/sudoers.d/postgres-repmgr'
|
|
102
|
+
execute :sudo, 'chmod', '440', '/etc/sudoers.d/postgres-repmgr'
|
|
103
|
+
execute :rm, '-f', '/tmp/postgres-repmgr-sudoers'
|
|
88
104
|
end
|
|
89
105
|
end
|
|
90
106
|
|
|
@@ -247,6 +263,7 @@ module ActivePostgres
|
|
|
247
263
|
effective_replication_password = replication_user == repmgr_user ? repmgr_password : replication_password
|
|
248
264
|
|
|
249
265
|
ensure_primary_registered
|
|
266
|
+
ensure_primary_replication_ready(repmgr_password, effective_replication_password)
|
|
250
267
|
|
|
251
268
|
setup_pgpass_file(standby_host, repmgr_password, replication_password: effective_replication_password,
|
|
252
269
|
primary_ip: primary_replication_host)
|
|
@@ -399,9 +416,42 @@ module ActivePostgres
|
|
|
399
416
|
end
|
|
400
417
|
|
|
401
418
|
register_standby_with_primary(standby_host)
|
|
419
|
+
setup_inter_node_ssh
|
|
402
420
|
enable_repmgrd_if_configured(standby_host, repmgr_config)
|
|
403
421
|
end
|
|
404
422
|
|
|
423
|
+
def setup_inter_node_ssh
|
|
424
|
+
dns_config = dns_failover_config
|
|
425
|
+
key_path = dns_config && dns_config[:ssh_key_path] || '/var/lib/postgresql/.ssh/active_postgres_dns'
|
|
426
|
+
postgres_user = config.postgres_user
|
|
427
|
+
all_hosts = config.all_hosts
|
|
428
|
+
pub_keys = {}
|
|
429
|
+
|
|
430
|
+
all_hosts.each do |host|
|
|
431
|
+
ssh_executor.execute_on_host(host) do
|
|
432
|
+
pub_keys[host] = capture(:sudo, '-u', postgres_user, 'cat', "#{key_path}.pub").strip
|
|
433
|
+
end
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
all_hosts.each do |host|
|
|
437
|
+
ssh_executor.execute_on_host(host) do
|
|
438
|
+
other_keys = pub_keys.reject { |h, _| h == host }.values
|
|
439
|
+
other_keys.each do |key|
|
|
440
|
+
next if key.to_s.empty?
|
|
441
|
+
|
|
442
|
+
upload! StringIO.new("#{key}\n"), '/tmp/pg_peer_key.pub'
|
|
443
|
+
execute :sudo, '-u', postgres_user, 'bash', '-c',
|
|
444
|
+
"grep -qxF -f /tmp/pg_peer_key.pub /var/lib/postgresql/.ssh/authorized_keys 2>/dev/null || cat /tmp/pg_peer_key.pub >> /var/lib/postgresql/.ssh/authorized_keys"
|
|
445
|
+
execute :rm, '-f', '/tmp/pg_peer_key.pub'
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
peer_ips = all_hosts.reject { |h| h == host }.map { |h| config.replication_host_for(h) }
|
|
449
|
+
scan_cmd = "ssh-keyscan #{peer_ips.join(' ')} >> /var/lib/postgresql/.ssh/known_hosts 2>/dev/null || true"
|
|
450
|
+
execute :sudo, '-u', postgres_user, 'bash', '-c', scan_cmd
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
|
|
405
455
|
def setup_dns_failover
|
|
406
456
|
dns_config = dns_failover_config
|
|
407
457
|
return unless dns_config
|
|
@@ -424,6 +474,8 @@ module ActivePostgres
|
|
|
424
474
|
authorize_dns_keys(dns_server, dns_user, pub_keys.values.compact)
|
|
425
475
|
end
|
|
426
476
|
|
|
477
|
+
setup_inter_node_ssh
|
|
478
|
+
|
|
427
479
|
config.all_hosts.each do |host|
|
|
428
480
|
install_dns_failover_script(host, dns_config, dns_private_ips, dns_user, dns_ssh_key_path, ssh_strict_host_key)
|
|
429
481
|
end
|
|
@@ -876,6 +928,33 @@ module ActivePostgres
|
|
|
876
928
|
is_registered
|
|
877
929
|
end
|
|
878
930
|
|
|
931
|
+
def ensure_primary_replication_ready(repmgr_password, effective_replication_password)
|
|
932
|
+
host = config.primary_host
|
|
933
|
+
repmgr_user = config.repmgr_user
|
|
934
|
+
repmgr_db = config.repmgr_database
|
|
935
|
+
replication_user = config.replication_user
|
|
936
|
+
repmgr_component = self
|
|
937
|
+
executor = ssh_executor
|
|
938
|
+
|
|
939
|
+
puts ' Ensuring repmgr user has correct password and privileges on primary...'
|
|
940
|
+
|
|
941
|
+
ssh_executor.execute_on_host(host) do
|
|
942
|
+
repmgr_sql = repmgr_component.send(:build_repmgr_setup_sql, repmgr_user, repmgr_db, repmgr_password)
|
|
943
|
+
executor.run_sql_on_backend(self, repmgr_sql, postgres_user: 'postgres', port: 5432, tuples_only: false,
|
|
944
|
+
capture: false)
|
|
945
|
+
|
|
946
|
+
if replication_user != repmgr_user
|
|
947
|
+
repl_sql = repmgr_component.send(:build_replication_user_sql, replication_user, effective_replication_password)
|
|
948
|
+
executor.run_sql_on_backend(self, repl_sql, postgres_user: 'postgres', port: 5432, tuples_only: false,
|
|
949
|
+
capture: false)
|
|
950
|
+
end
|
|
951
|
+
|
|
952
|
+
info '✓ Primary replication user is ready'
|
|
953
|
+
end
|
|
954
|
+
|
|
955
|
+
setup_pgpass_file(host, repmgr_password, replication_password: effective_replication_password)
|
|
956
|
+
end
|
|
957
|
+
|
|
879
958
|
def regenerate_ssl_certs(host, version)
|
|
880
959
|
ssl_config = config.component_config(:ssl)
|
|
881
960
|
ssl_cert = secrets.resolve('ssl_cert')
|
|
@@ -1011,9 +1090,9 @@ module ActivePostgres
|
|
|
1011
1090
|
'DO $$',
|
|
1012
1091
|
'BEGIN',
|
|
1013
1092
|
" IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = '#{repmgr_user}') THEN",
|
|
1014
|
-
" CREATE USER #{repmgr_user} WITH SUPERUSER PASSWORD '#{escaped_password}';",
|
|
1093
|
+
" CREATE USER #{repmgr_user} WITH SUPERUSER REPLICATION PASSWORD '#{escaped_password}';",
|
|
1015
1094
|
' ELSE',
|
|
1016
|
-
" ALTER USER #{repmgr_user} WITH SUPERUSER PASSWORD '#{escaped_password}';",
|
|
1095
|
+
" ALTER USER #{repmgr_user} WITH SUPERUSER REPLICATION PASSWORD '#{escaped_password}';",
|
|
1017
1096
|
' END IF;',
|
|
1018
1097
|
'END $$;',
|
|
1019
1098
|
'',
|
|
@@ -81,6 +81,8 @@ module ActivePostgres
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def fetch_from_rails_credentials(key_path)
|
|
84
|
+
ensure_rails_credentials_available
|
|
85
|
+
|
|
84
86
|
return nil unless defined?(::Rails) && ::Rails.respond_to?(:application) && ::Rails.application
|
|
85
87
|
|
|
86
88
|
keys = key_path.split('.').map(&:to_sym)
|
|
@@ -89,6 +91,22 @@ module ActivePostgres
|
|
|
89
91
|
nil
|
|
90
92
|
end
|
|
91
93
|
|
|
94
|
+
def ensure_rails_credentials_available
|
|
95
|
+
return if @rails_boot_attempted
|
|
96
|
+
return if defined?(::Rails) && ::Rails.respond_to?(:application) && ::Rails.application
|
|
97
|
+
|
|
98
|
+
@rails_boot_attempted = true
|
|
99
|
+
|
|
100
|
+
# Try loading just the Rails application (without full initialization)
|
|
101
|
+
# This makes Rails.application available for credential access
|
|
102
|
+
if File.exist?('./config/application.rb')
|
|
103
|
+
require './config/application'
|
|
104
|
+
end
|
|
105
|
+
rescue StandardError
|
|
106
|
+
# Rails boot failed — CLI running outside a Rails project
|
|
107
|
+
nil
|
|
108
|
+
end
|
|
109
|
+
|
|
92
110
|
def execute_command(command)
|
|
93
111
|
# Preserve RAILS_ENV if set
|
|
94
112
|
env_prefix = ENV['RAILS_ENV'] ? "RAILS_ENV=#{ENV['RAILS_ENV']} " : ''
|
data/templates/repmgr.conf.erb
CHANGED
|
@@ -30,6 +30,13 @@ follow_command='repmgr standby follow -f /etc/repmgr.conf --upstream-node-id=%n'
|
|
|
30
30
|
|
|
31
31
|
reconnect_attempts=<%= repmgr_config[:reconnect_attempts] || 6 %>
|
|
32
32
|
reconnect_interval=<%= repmgr_config[:reconnect_interval] || 10 %>
|
|
33
|
+
|
|
34
|
+
service_start_command='sudo systemctl start postgresql@<%= config.version %>-main'
|
|
35
|
+
service_stop_command='sudo systemctl stop postgresql@<%= config.version %>-main'
|
|
36
|
+
service_restart_command='sudo systemctl restart postgresql@<%= config.version %>-main'
|
|
37
|
+
service_reload_command='sudo systemctl reload postgresql@<%= config.version %>-main'
|
|
38
|
+
|
|
39
|
+
ssh_options='-i /var/lib/postgresql/.ssh/active_postgres_dns -o StrictHostKeyChecking=no'
|
|
33
40
|
<% if repmgr_config.key?(:use_rewind) %>
|
|
34
41
|
use_rewind=<%= repmgr_config[:use_rewind] ? 'yes' : 'no' %>
|
|
35
42
|
<% end %>
|
|
@@ -12,13 +12,17 @@ SSH_STRICT_HOST_KEY="<%= ssh_strict_host_key %>"
|
|
|
12
12
|
SSH_KNOWN_HOSTS="/var/lib/postgresql/.ssh/known_hosts"
|
|
13
13
|
LOG_TAG="active_postgres_dns"
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
REPMGR_DB=$(grep -oP "dbname=\K[^ ']+" "$REPMGR_CONF" | head -1)
|
|
16
|
+
REPMGR_USER=$(grep -oP "user=\K[^ ']+" "$REPMGR_CONF" 2>/dev/null | head -1)
|
|
17
|
+
REPMGR_USER="${REPMGR_USER:-repmgr}"
|
|
18
|
+
|
|
19
|
+
cluster_data=$(sudo -u postgres psql -h /var/run/postgresql -d "$REPMGR_DB" -tAF',' -c "SELECT type, conninfo FROM repmgr.nodes WHERE active = true" 2>/dev/null || true)
|
|
20
|
+
if [[ -z "$cluster_data" ]]; then
|
|
17
21
|
exit 0
|
|
18
22
|
fi
|
|
19
23
|
|
|
20
|
-
primary_host=$(printf "%s\n" "$
|
|
21
|
-
standby_hosts=$(printf "%s\n" "$
|
|
24
|
+
primary_host=$(printf "%s\n" "$cluster_data" | awk -F',' '$1 == "primary" {print $2; exit}' | sed -n 's/.*host=\([^ ]*\).*/\1/p')
|
|
25
|
+
standby_hosts=$(printf "%s\n" "$cluster_data" | awk -F',' '$1 == "standby" {print $2}' | sed -n 's/.*host=\([^ ]*\).*/\1/p' | sort -u)
|
|
22
26
|
|
|
23
27
|
if [[ -z "$primary_host" ]]; then
|
|
24
28
|
exit 0
|
|
@@ -51,7 +55,7 @@ for server in "${DNS_SERVERS[@]}"; do
|
|
|
51
55
|
fi
|
|
52
56
|
|
|
53
57
|
if ! /usr/bin/ssh "${ssh_opts[@]}" "${DNS_USER}@${server}" \
|
|
54
|
-
"sudo bash -c 'cat > ${DNSMASQ_FILE} << \"EOF\"\\n${content}EOF\\n' &&
|
|
58
|
+
"sudo bash -c 'cat > ${DNSMASQ_FILE} << \"EOF\"\\n${content}EOF\\n' && sudo systemctl restart dnsmasq"; then
|
|
55
59
|
/usr/bin/logger -t "$LOG_TAG" "Failed updating dnsmasq on ${server}"
|
|
56
60
|
fi
|
|
57
61
|
done
|