active_postgres 0.9.0 → 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.
@@ -1,6 +1,7 @@
1
1
  require 'sshkit'
2
2
  require 'sshkit/dsl'
3
3
  require 'securerandom'
4
+ require 'stringio'
4
5
 
5
6
  module ActivePostgres
6
7
  class SSHExecutor
@@ -294,18 +295,21 @@ module ActivePostgres
294
295
  SSHKit.config.format = :pretty
295
296
  end
296
297
 
297
- return unless File.exist?(config.ssh_key)
298
-
299
298
  SSHKit::Backend::Netssh.configure do |ssh|
300
- ssh.ssh_options = {
301
- keys: [config.ssh_key],
302
- keys_only: true,
299
+ options = {
303
300
  forward_agent: false,
304
301
  auth_methods: ['publickey'],
305
302
  verify_host_key: config.ssh_host_key_verification || :always,
306
303
  timeout: 10,
307
304
  number_of_password_prompts: 0
308
305
  }
306
+
307
+ if config.ssh_key && File.exist?(config.ssh_key)
308
+ options[:keys] = [config.ssh_key]
309
+ options[:keys_only] = true
310
+ end
311
+
312
+ ssh.ssh_options = options
309
313
  end
310
314
  end
311
315
  end
@@ -1,3 +1,3 @@
1
1
  module ActivePostgres
2
- VERSION = '0.9.0'.freeze
2
+ VERSION = '0.9.2'.freeze
3
3
  end
@@ -23,6 +23,7 @@ require_relative 'active_postgres/installer'
23
23
  require_relative 'active_postgres/ssh_executor'
24
24
  require_relative 'active_postgres/direct_executor'
25
25
  require_relative 'active_postgres/health_checker'
26
+ require_relative 'active_postgres/overview'
26
27
  require_relative 'active_postgres/failover'
27
28
  require_relative 'active_postgres/performance_tuner'
28
29
  require_relative 'active_postgres/connection_pooler'
@@ -150,6 +150,15 @@ namespace :postgres do
150
150
  health_checker.show_status
151
151
  end
152
152
 
153
+ desc 'Show control tower overview'
154
+ task overview: :environment do
155
+ require 'active_postgres'
156
+
157
+ config = ActivePostgres::Configuration.load
158
+ overview = ActivePostgres::Overview.new(config)
159
+ overview.show
160
+ end
161
+
153
162
  desc 'Visualize cluster nodes and topology'
154
163
  task nodes: :environment do
155
164
  require 'active_postgres'
@@ -270,6 +279,50 @@ namespace :postgres do
270
279
  puts "\n#{'=' * 80}\n"
271
280
  end
272
281
 
282
+ desc 'Show help for PostgreSQL rake tasks'
283
+ task help: :environment do
284
+ puts "\nPostgreSQL Rake Tasks"
285
+ puts '=' * 70
286
+ puts "\nTip: set RAILS_ENV=production for production targets"
287
+
288
+ puts "\nSetup & Maintenance"
289
+ puts " rake postgres:setup # Deploy HA cluster (CLEAN=true for fresh install)"
290
+ puts " rake postgres:purge # Destroy cluster (DESTRUCTIVE)"
291
+ puts " rake postgres:setup:core # PostgreSQL config (postgresql.conf, pg_hba.conf)"
292
+ puts " rake postgres:setup:repmgr # HA + failover (repmgr)"
293
+ puts " rake postgres:setup:pgbouncer # PgBouncer pooling"
294
+ puts " rake postgres:setup:pgbackrest # Backups (pgBackRest)"
295
+ puts " rake postgres:setup:monitoring # postgres_exporter"
296
+ puts " rake postgres:setup:ssl # SSL certs"
297
+
298
+ puts "\nStatus & Health"
299
+ puts " rake postgres:status # Cluster status (SSH by default)"
300
+ puts " rake postgres:overview # Control tower overview"
301
+ puts " rake postgres:nodes # Topology view"
302
+ puts " rake postgres:verify # Comprehensive checklist"
303
+ puts " ACTIVE_POSTGRES_STATUS_MODE=ssh|direct rake postgres:status"
304
+
305
+ puts "\nBackups"
306
+ puts " rake postgres:backup:full # Full backup"
307
+ puts " rake postgres:backup:incremental # Incremental backup"
308
+ puts " rake postgres:backup:list # List backups"
309
+ puts " rake postgres:backup:restore[ID] # Restore backup set"
310
+ puts " rake postgres:backup:restore_at[\"YYYY-MM-DD HH:MM:SS\",promote] # PITR"
311
+
312
+ puts "\nMigrations"
313
+ puts " rake postgres:migrate # Run migrations on primary only"
314
+
315
+ puts "\nPgBouncer"
316
+ puts " rake postgres:pgbouncer:update_userlist[users] # Update userlist"
317
+ puts " rake postgres:pgbouncer:stats # Service status + stats"
318
+
319
+ puts "\nTests"
320
+ puts " rake postgres:test:replication[rows] # Replication stress test"
321
+ puts " rake postgres:test:pgbouncer[connections] # PgBouncer load test"
322
+
323
+ puts
324
+ end
325
+
273
326
  desc 'Promote standby to primary'
274
327
  task :promote, [:host] => :environment do |_t, args|
275
328
  require 'active_postgres'
@@ -325,6 +378,20 @@ namespace :postgres do
325
378
  installer.run_restore(args[:backup_id])
326
379
  end
327
380
 
381
+ desc 'Restore to a point in time (PITR)'
382
+ task :restore_at, [:target_time, :target_action] => :environment do |_t, args|
383
+ require 'active_postgres'
384
+
385
+ unless args[:target_time]
386
+ puts 'Usage: rake postgres:backup:restore_at["2026-01-29 01:15:00",promote]'
387
+ exit 1
388
+ end
389
+
390
+ config = ActivePostgres::Configuration.load
391
+ installer = ActivePostgres::Installer.new(config)
392
+ installer.run_restore_at(args[:target_time], target_action: args[:target_action] || 'promote')
393
+ end
394
+
328
395
  desc 'List available backups'
329
396
  task list: :environment do
330
397
  require 'active_postgres'
@@ -31,6 +31,16 @@ follow_command='repmgr standby follow -f /etc/repmgr.conf --upstream-node-id=%n'
31
31
  reconnect_attempts=<%= repmgr_config[:reconnect_attempts] || 6 %>
32
32
  reconnect_interval=<%= repmgr_config[:reconnect_interval] || 10 %>
33
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'
40
+ <% if repmgr_config.key?(:use_rewind) %>
41
+ use_rewind=<%= repmgr_config[:use_rewind] ? 'yes' : 'no' %>
42
+ <% end %>
43
+
34
44
  log_level=INFO
35
45
  log_facility=STDERR
36
46
  log_file='/var/log/postgresql/repmgr.log'
@@ -6,30 +6,44 @@ DNS_USER="<%= dns_user %>"
6
6
  DNS_SERVERS=(<%= dns_servers.map(&:to_s).join(' ') %>)
7
7
  DNS_SSH_KEY="<%= dns_ssh_key_path %>"
8
8
  DNSMASQ_FILE="/etc/dnsmasq.d/active_postgres.conf"
9
- PRIMARY_RECORD="<%= primary_record %>"
10
- REPLICA_RECORD="<%= replica_record %>"
9
+ PRIMARY_RECORDS=(<%= primary_records.map(&:to_s).join(' ') %>)
10
+ REPLICA_RECORDS=(<%= replica_records.map(&:to_s).join(' ') %>)
11
11
  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
- cluster_csv=$(repmgr -f "$REPMGR_CONF" cluster show --csv 2>/dev/null || true)
16
- if [[ -z "$cluster_csv" ]]; then
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" "$cluster_csv" | awk -F',' 'NR>1 && tolower($3) ~ /primary/ {print $NF; exit}' | sed -n 's/.*host=\\([^ ]*\\).*/\\1/p')
21
- standby_hosts=$(printf "%s\n" "$cluster_csv" | awk -F',' 'NR>1 && tolower($3) ~ /standby/ {print $NF}' | sed -n 's/.*host=\\([^ ]*\\).*/\\1/p' | sort -u)
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
25
29
  fi
26
30
 
27
31
  content=$'# Managed by active_postgres\n'
28
- printf -v content '%saddress=/%s/%s\n' "$content" "$PRIMARY_RECORD" "$primary_host"
32
+ for record in "${PRIMARY_RECORDS[@]}"; do
33
+ if [[ -n "$record" ]]; then
34
+ printf -v content '%saddress=/%s/%s\n' "$content" "$record" "$primary_host"
35
+ fi
36
+ done
29
37
 
30
38
  if [[ -n "$standby_hosts" ]]; then
31
- for host in $standby_hosts; do
32
- printf -v content '%saddress=/%s/%s\n' "$content" "$REPLICA_RECORD" "$host"
39
+ for record in "${REPLICA_RECORDS[@]}"; do
40
+ if [[ -z "$record" ]]; then
41
+ continue
42
+ fi
43
+
44
+ for host in $standby_hosts; do
45
+ printf -v content '%saddress=/%s/%s\n' "$content" "$record" "$host"
46
+ done
33
47
  done
34
48
  fi
35
49
 
@@ -41,9 +55,9 @@ for server in "${DNS_SERVERS[@]}"; do
41
55
  fi
42
56
 
43
57
  if ! /usr/bin/ssh "${ssh_opts[@]}" "${DNS_USER}@${server}" \
44
- "sudo bash -c 'cat > ${DNSMASQ_FILE} << \"EOF\"\\n${content}EOF\\n' && (sudo systemctl reload dnsmasq || sudo systemctl restart dnsmasq)"; then
58
+ "sudo bash -c 'cat > ${DNSMASQ_FILE} << \"EOF\"\\n${content}EOF\\n' && sudo systemctl restart dnsmasq"; then
45
59
  /usr/bin/logger -t "$LOG_TAG" "Failed updating dnsmasq on ${server}"
46
60
  fi
47
61
  done
48
62
 
49
- /usr/bin/logger -t "$LOG_TAG" "Updated DNS records for ${PRIMARY_RECORD} and ${REPLICA_RECORD} (primary=${primary_host})"
63
+ /usr/bin/logger -t "$LOG_TAG" "Updated DNS records for ${PRIMARY_RECORDS[*]} and ${REPLICA_RECORDS[*]} (primary=${primary_host})"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_postgres
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - BoringCache
@@ -179,6 +179,7 @@ files:
179
179
  - lib/active_postgres/installer.rb
180
180
  - lib/active_postgres/log_sanitizer.rb
181
181
  - lib/active_postgres/logger.rb
182
+ - lib/active_postgres/overview.rb
182
183
  - lib/active_postgres/performance_tuner.rb
183
184
  - lib/active_postgres/rails/database_config.rb
184
185
  - lib/active_postgres/rails/migration_guard.rb