active_postgres 0.4.0 → 0.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dbeebfa934a6587194e20aa39a3d459b7db2568c996f4faec1b781d70a4254ee
4
- data.tar.gz: 47867c12a1fbb44652da9a79bb775ce38aaf8ff7b527a0a14ce0e042ca091877
3
+ metadata.gz: cccb8eff8b3ff2ae0f3413bef89b81a1b5b487e474222a9ff3e85fc74e57c5e9
4
+ data.tar.gz: 05a21ba9f1a29b6b5d451b3c059fd6a15135b8d6ecd7bad7d6232e6c30a5d63a
5
5
  SHA512:
6
- metadata.gz: cd6849746528e378cc7e5d3c7707b20d974dc44cc10e61f4600b7eaccbfd7c9793e20b8741ae7339ab866618aa7357ee6781bf4d7ba7ee0eff959ad9c0af1b24
7
- data.tar.gz: fc7635c0908c5920e4f21f702badd030c305819b05e63e6a0a6b54793b563e7338738445d38f11e1269566afba9fb0e6efcbea436287ace088518f6bce42a777
6
+ metadata.gz: '05819a2a73506fb087831e9ad69b6901b8bd6f14f0559a8e08d8c7fbbbff5c20eabc616bf0c862dd95757dab18a52666d8c0c1c00a63dc23b2094849160ce80b'
7
+ data.tar.gz: 2733384099851358c38788c41f77768e7a781cd99b7492aa4c10c85758a520167ad954657e0842d7b55681b2f932515933e887706ba3e2d39a50c44fdbd1e7e5
@@ -54,6 +54,9 @@ module ActivePostgres
54
54
 
55
55
  # Create application users AFTER repmgr to avoid being wiped by cluster recreation
56
56
  create_application_users_if_configured
57
+
58
+ # Update pgbouncer userlist AFTER app users are created so they can authenticate
59
+ update_pgbouncer_userlist if config.component_enabled?(:pgbouncer)
57
60
  end
58
61
 
59
62
  def list_next_steps
@@ -81,5 +84,12 @@ module ActivePostgres
81
84
  core_component = Components::Core.new(config, ssh_executor, secrets)
82
85
  core_component.create_application_users
83
86
  end
87
+
88
+ def update_pgbouncer_userlist
89
+ logger.task('Updating PgBouncer userlist with app users') do
90
+ component = Components::PgBouncer.new(config, ssh_executor, secrets)
91
+ component.update_userlist
92
+ end
93
+ end
84
94
  end
85
95
  end
@@ -11,11 +11,16 @@ module ActivePostgres
11
11
  # See: create_application_user_and_database method called from deployment flow
12
12
 
13
13
  # Install on standbys
14
- # If repmgr is enabled, only install packages (cluster will be cloned by repmgr)
15
- # If repmgr is disabled, install everything including cluster creation
16
14
  config.standby_hosts.each do |host|
17
15
  if config.component_enabled?(:repmgr)
18
- install_packages_only(host)
16
+ # Check if cluster already exists (config update vs fresh install)
17
+ if cluster_exists?(host)
18
+ # Existing cluster - just update configs
19
+ update_configs_on_host(host)
20
+ else
21
+ # Fresh install - only install packages, repmgr will clone the cluster
22
+ install_packages_only(host)
23
+ end
19
24
  else
20
25
  install_on_host(host, is_primary: false)
21
26
  end
@@ -62,6 +67,10 @@ module ActivePostgres
62
67
  else
63
68
  component_config[:postgresql] || {}
64
69
  end
70
+
71
+ # Substitute ${private_ip} with the host's actual private IP
72
+ private_ip = config.replication_host_for(host)
73
+ pg_config = substitute_private_ip(pg_config, private_ip)
65
74
  _ = pg_config # Used in ERB template
66
75
 
67
76
  upload_template(host, 'postgresql.conf.erb', "/etc/postgresql/#{config.version}/main/postgresql.conf", binding,
@@ -87,11 +96,99 @@ module ActivePostgres
87
96
  optimal_settings.merge(user_postgresql)
88
97
  end
89
98
 
99
+ def substitute_private_ip(pg_config, private_ip)
100
+ pg_config.transform_values do |value|
101
+ if value.is_a?(String)
102
+ value.gsub('${private_ip}', private_ip)
103
+ else
104
+ value
105
+ end
106
+ end
107
+ end
108
+
90
109
  def install_packages_only(host)
91
110
  puts " Installing packages on #{host} (cluster will be created by repmgr)..."
92
111
  ssh_executor.install_postgres(host, config.version)
93
112
  end
94
113
 
114
+ def cluster_exists?(host)
115
+ exists = false
116
+ version = config.version
117
+ ssh_executor.execute_on_host(host) do
118
+ exists = test(:sudo, 'test', '-d', "/var/lib/postgresql/#{version}/main/base")
119
+ end
120
+ exists
121
+ end
122
+
123
+ def update_configs_on_host(host)
124
+ puts " Updating configs on #{host}..."
125
+
126
+ component_config = config.component_config(:core)
127
+
128
+ pg_config = if config.component_enabled?(:performance_tuning)
129
+ tuned = calculate_tuned_settings(host, component_config)
130
+ # Standbys must have replication-critical settings >= primary
131
+ ensure_standby_compatible(tuned)
132
+ else
133
+ component_config[:postgresql] || {}
134
+ end
135
+
136
+ private_ip = config.replication_host_for(host)
137
+ pg_config = substitute_private_ip(pg_config, private_ip)
138
+ _ = pg_config
139
+
140
+ upload_template(host, 'postgresql.conf.erb', "/etc/postgresql/#{config.version}/main/postgresql.conf", binding,
141
+ owner: 'postgres:postgres')
142
+ upload_template(host, 'pg_hba.conf.erb', "/etc/postgresql/#{config.version}/main/pg_hba.conf", binding,
143
+ owner: 'postgres:postgres')
144
+
145
+ ssh_executor.restart_postgres(host, config.version)
146
+ end
147
+
148
+ def ensure_standby_compatible(pg_config)
149
+ primary_settings = get_primary_replication_settings
150
+ return pg_config if primary_settings.empty?
151
+
152
+ adjustments = []
153
+
154
+ %i[max_connections max_worker_processes max_wal_senders
155
+ max_prepared_transactions max_locks_per_transaction].each do |setting|
156
+ primary_val = primary_settings[setting]
157
+ next unless primary_val
158
+
159
+ current_val = pg_config[setting]
160
+ if current_val.nil? || current_val.to_i < primary_val.to_i
161
+ adjustments << "#{setting}: #{current_val || 'unset'} → #{primary_val}"
162
+ pg_config[setting] = primary_val
163
+ end
164
+ end
165
+
166
+ if adjustments.any?
167
+ puts " ⚠️ Adjusting standby settings to match primary minimum:"
168
+ adjustments.each { |adj| puts " #{adj}" }
169
+ end
170
+
171
+ pg_config
172
+ end
173
+
174
+ def get_primary_replication_settings
175
+ @primary_replication_settings ||= begin
176
+ settings = {}
177
+ sql = "SELECT name || '=' || setting FROM pg_settings WHERE name IN ('max_connections', 'max_worker_processes', 'max_wal_senders', 'max_prepared_transactions', 'max_locks_per_transaction')"
178
+ result = ssh_executor.run_sql(config.primary_host, sql)
179
+ result.strip.split("\n").each do |line|
180
+ name, val = line.split('=')
181
+ next unless name && val
182
+
183
+ settings[name.strip.to_sym] = val.strip.to_i
184
+ end
185
+ settings
186
+ rescue StandardError => e
187
+ puts " Warning: Could not get primary settings: #{e.message}"
188
+ {}
189
+ end
190
+ end
191
+
95
192
  def create_app_user_and_database(host)
96
193
  app_user = config.app_user
97
194
  app_database = config.app_database
@@ -4,14 +4,22 @@ module ActivePostgres
4
4
  def install
5
5
  puts 'Installing pgBackRest for backups...'
6
6
 
7
- install_on_host(config.primary_host)
7
+ # Install on primary with full setup (stanza-create)
8
+ install_on_host(config.primary_host, create_stanza: true)
9
+
10
+ # Install on standbys (package + config only, no stanza-create)
11
+ config.standby_hosts.each do |host|
12
+ install_on_host(host, create_stanza: false)
13
+ end
8
14
  end
9
15
 
10
16
  def uninstall
11
17
  puts 'Uninstalling pgBackRest...'
12
18
 
13
- ssh_executor.execute_on_host(config.primary_host) do
14
- execute :sudo, 'apt-get', 'remove', '-y', 'pgbackrest'
19
+ config.all_hosts.each do |host|
20
+ ssh_executor.execute_on_host(host) do
21
+ execute :sudo, 'apt-get', 'remove', '-y', 'pgbackrest'
22
+ end
15
23
  end
16
24
  end
17
25
 
@@ -55,7 +63,7 @@ module ActivePostgres
55
63
 
56
64
  private
57
65
 
58
- def install_on_host(host)
66
+ def install_on_host(host, create_stanza: true)
59
67
  puts " Installing pgBackRest on #{host}..."
60
68
 
61
69
  pgbackrest_config = config.component_config(:pgbackrest)
@@ -86,7 +94,10 @@ module ActivePostgres
86
94
  execute :sudo, 'mkdir', '-p', '/var/spool/pgbackrest'
87
95
  execute :sudo, 'chown', "#{postgres_user}:#{postgres_user}", '/var/spool/pgbackrest'
88
96
 
89
- execute :sudo, '-u', postgres_user, 'pgbackrest', '--stanza=main', 'stanza-create'
97
+ # Only create stanza on primary - standbys share the same backup repo
98
+ if create_stanza
99
+ execute :sudo, '-u', postgres_user, 'pgbackrest', '--stanza=main', 'stanza-create'
100
+ end
90
101
  end
91
102
  end
92
103
  end
@@ -4,27 +4,49 @@ module ActivePostgres
4
4
  def install
5
5
  puts 'Installing PgBouncer for connection pooling...'
6
6
 
7
- # Install on primary (can also install on standbys if needed)
8
- install_on_host(config.primary_host)
7
+ config.all_hosts.each do |host|
8
+ install_on_host(host)
9
+ end
9
10
  end
10
11
 
11
12
  def uninstall
12
13
  puts 'Uninstalling PgBouncer...'
13
14
 
14
- ssh_executor.execute_on_host(config.primary_host) do
15
- execute :sudo, 'systemctl', 'stop', 'pgbouncer'
16
- execute :sudo, 'apt-get', 'remove', '-y', 'pgbouncer'
15
+ config.all_hosts.each do |host|
16
+ ssh_executor.execute_on_host(host) do
17
+ execute :sudo, 'systemctl', 'stop', 'pgbouncer'
18
+ execute :sudo, 'apt-get', 'remove', '-y', 'pgbouncer'
19
+ end
17
20
  end
18
21
  end
19
22
 
20
23
  def restart
21
24
  puts 'Restarting PgBouncer...'
22
25
 
23
- ssh_executor.execute_on_host(config.primary_host) do
24
- execute :sudo, 'systemctl', 'restart', 'pgbouncer'
26
+ config.all_hosts.each do |host|
27
+ ssh_executor.execute_on_host(host) do
28
+ execute :sudo, 'systemctl', 'restart', 'pgbouncer'
29
+ end
25
30
  end
26
31
  end
27
32
 
33
+ def update_userlist
34
+ puts 'Updating PgBouncer userlist on all hosts...'
35
+
36
+ config.all_hosts.each do |host|
37
+ create_userlist(host)
38
+
39
+ ssh_executor.execute_on_host(host) do
40
+ execute :sudo, 'systemctl', 'reload', 'pgbouncer'
41
+ end
42
+ end
43
+ end
44
+
45
+ def install_on_standby(standby_host)
46
+ puts "Installing PgBouncer on standby #{standby_host}..."
47
+ install_on_host(standby_host)
48
+ end
49
+
28
50
  private
29
51
 
30
52
  def install_on_host(host)
@@ -14,7 +14,6 @@ module ActivePostgres
14
14
  end
15
15
 
16
16
  def restart
17
- # SSL doesn't have its own service, restart PostgreSQL
18
17
  ssh_executor.restart_postgres(config.primary_host)
19
18
 
20
19
  config.standby_hosts.each do |host|
@@ -37,7 +36,6 @@ module ActivePostgres
37
36
 
38
37
  ssh_executor.ensure_postgres_user(host)
39
38
 
40
- # Ensure the PostgreSQL config directory exists
41
39
  ssh_executor.execute_on_host(host) do
42
40
  execute :sudo, 'mkdir', '-p', "/etc/postgresql/#{version}/main"
43
41
  execute :sudo, 'chown', 'postgres:postgres', "/etc/postgresql/#{version}/main"
@@ -47,17 +45,31 @@ module ActivePostgres
47
45
  ssl_key = secrets.resolve('ssl_key')
48
46
 
49
47
  if ssl_cert && ssl_key
50
- puts ' Using SSL certificates from secrets...'
51
- ssh_executor.upload_file(host, ssl_cert, "/etc/postgresql/#{version}/main/server.crt", mode: '644',
52
- owner: 'postgres:postgres')
53
- ssh_executor.upload_file(host, ssl_key, "/etc/postgresql/#{version}/main/server.key", mode: '600',
54
- owner: 'postgres:postgres')
48
+ install_custom_cert(host, ssl_cert, ssl_key, ssl_config)
55
49
  else
56
50
  puts ' Generating self-signed SSL certificates...'
57
51
  generate_self_signed_cert(host, ssl_config)
58
52
  end
59
53
  end
60
54
 
55
+ def install_custom_cert(host, ssl_cert, ssl_key, _ssl_config)
56
+ version = config.version
57
+ ssl_chain = secrets.resolve('ssl_chain')
58
+
59
+ puts ' Using SSL certificates from secrets...'
60
+
61
+ full_cert = if ssl_chain
62
+ "#{ssl_cert.strip}\n#{ssl_chain.strip}\n"
63
+ else
64
+ ssl_cert
65
+ end
66
+
67
+ ssh_executor.upload_file(host, full_cert, "/etc/postgresql/#{version}/main/server.crt",
68
+ mode: '644', owner: 'postgres:postgres')
69
+ ssh_executor.upload_file(host, ssl_key, "/etc/postgresql/#{version}/main/server.key",
70
+ mode: '600', owner: 'postgres:postgres')
71
+ end
72
+
61
73
  def generate_self_signed_cert(host, ssl_config)
62
74
  version = config.version
63
75
  cert_path = "/etc/postgresql/#{version}/main/server.crt"
@@ -0,0 +1 @@
1
+ <%= ActivePostgres::Rails::DatabaseConfig.render_partial('production', app_name: boring_app_name).strip %>
@@ -0,0 +1,85 @@
1
+ # PostgreSQL High Availability Configuration
2
+ # Generated by active_postgres
3
+ #
4
+ # 💡 Either:
5
+ # 1. Edit this file manually with your database servers
6
+ # 2. Use Terraform to auto-generate this file
7
+ #
8
+ # See: config/postgres.example.yml in gem for full examples
9
+
10
+ shared: &shared
11
+ version: 18
12
+ user: ubuntu
13
+ ssh_key: ~/.ssh/id_rsa
14
+
15
+ components:
16
+ core:
17
+ locale: en_US.UTF-8
18
+ encoding: UTF8
19
+ # Optional: Override default PostgreSQL user and app database config
20
+ # postgres_user: postgres
21
+ # app_user: app
22
+ # app_database: app_production
23
+ postgresql:
24
+ listen_addresses: '*'
25
+ port: 5432
26
+ max_connections: 100
27
+ shared_buffers: 256MB
28
+
29
+ repmgr:
30
+ enabled: false
31
+ # Optional: Override default repmgr user/database
32
+ # user: repmgr
33
+ # database: repmgr
34
+
35
+ pgbouncer:
36
+ enabled: false
37
+ # Optional: Override default pgbouncer user
38
+ # user: pgbouncer
39
+
40
+ pgbackrest:
41
+ enabled: false
42
+
43
+ monitoring:
44
+ enabled: false
45
+
46
+ ssl:
47
+ enabled: false
48
+
49
+ development:
50
+ <<: *shared
51
+ primary:
52
+ host: localhost
53
+ port: 5432
54
+
55
+ production:
56
+ <<: *shared
57
+
58
+ # TODO: Configure your primary database server
59
+ # host: Public IP for SSH deployment (like Kamal)
60
+ # private_ip: Private/VPC IP for database connections (optional, falls back to host)
61
+ primary:
62
+ host: YOUR_PRIMARY_PUBLIC_IP
63
+ private_ip: YOUR_PRIMARY_PRIVATE_IP
64
+ label: us-east-1
65
+
66
+ # TODO: Add standby servers for HA (optional)
67
+ # standby:
68
+ # - host: YOUR_STANDBY_PUBLIC_IP
69
+ # private_ip: YOUR_STANDBY_PRIVATE_IP
70
+ # label: us-west-2
71
+
72
+ # Enable components as needed
73
+ components:
74
+ repmgr:
75
+ enabled: false # Enable for HA with standbys
76
+
77
+ # Secrets: Using Rails credentials (update via: rails credentials:edit)
78
+ # The rails_credentials: prefix fetches values from Rails.application.credentials
79
+ secrets:
80
+ superuser_password: rails_credentials:postgres.superuser_password
81
+ replication_password: rails_credentials:postgres.replication_password
82
+ repmgr_password: rails_credentials:postgres.repmgr_password
83
+ pgbouncer_password: rails_credentials:postgres.password
84
+ app_password: rails_credentials:postgres.password
85
+
@@ -51,6 +51,27 @@ module ActivePostgres
51
51
  end
52
52
  end
53
53
 
54
+ # Check pgbouncer on all hosts
55
+ if config.component_enabled?(:pgbouncer)
56
+ puts
57
+ puts '==> Checking PgBouncer...'
58
+ config.all_hosts.each do |host|
59
+ print "PgBouncer (#{host})... "
60
+ pgbouncer_ok = check_pgbouncer_running(host)
61
+ userlist_ok = check_pgbouncer_userlist(host)
62
+
63
+ if pgbouncer_ok && userlist_ok
64
+ puts '✓'
65
+ elsif pgbouncer_ok && !userlist_ok
66
+ puts '✗ (missing app user in userlist)'
67
+ all_ok = false
68
+ else
69
+ puts '✗'
70
+ all_ok = false
71
+ end
72
+ end
73
+ end
74
+
54
75
  puts
55
76
  if all_ok
56
77
  puts '✓ All checks passed'
@@ -99,7 +120,8 @@ module ActivePostgres
99
120
  label: primary_config&.dig('label') || '-',
100
121
  status: check_postgres_running(config.primary_host) ? '✓ running' : '✗ down',
101
122
  connections: get_connection_count(config.primary_host),
102
- lag: '-'
123
+ lag: '-',
124
+ pgbouncer: check_pgbouncer_status(config.primary_host)
103
125
  }
104
126
 
105
127
  # Standbys
@@ -114,7 +136,8 @@ module ActivePostgres
114
136
  label: standby_config&.dig('label') || '-',
115
137
  status: running ? '✓ streaming' : '✗ down',
116
138
  connections: running ? get_connection_count(host) : 0,
117
- lag: running ? get_replication_lag(host) : '-'
139
+ lag: running ? get_replication_lag(host) : '-',
140
+ pgbouncer: check_pgbouncer_status(host)
118
141
  }
119
142
  end
120
143
 
@@ -128,7 +151,7 @@ module ActivePostgres
128
151
  end
129
152
 
130
153
  def calculate_column_widths(nodes)
131
- {
154
+ cols = {
132
155
  role: [4, nodes.map { |n| n[:role].length }.max].max,
133
156
  host: [4, nodes.map { |n| n[:host].length }.max].max,
134
157
  private_ip: [10, nodes.map { |n| n[:private_ip].to_s.length }.max].max,
@@ -137,12 +160,25 @@ module ActivePostgres
137
160
  conn: 5,
138
161
  lag: [3, nodes.map { |n| n[:lag].to_s.length }.max].max
139
162
  }
163
+
164
+ if config.component_enabled?(:pgbouncer)
165
+ cols[:pgbouncer] = [9, nodes.map { |n| n[:pgbouncer].to_s.length }.max].max
166
+ end
167
+
168
+ cols
140
169
  end
141
170
 
142
171
  def print_table_header(cols)
143
172
  fmt = "%-#{cols[:role]}s %-#{cols[:host]}s %-#{cols[:private_ip]}s " \
144
173
  "%-#{cols[:label]}s %-#{cols[:status]}s %#{cols[:conn]}s %#{cols[:lag]}s"
145
- header = format(fmt, 'Role', 'Host', 'Private IP', 'Label', 'Status', 'Conn', 'Lag')
174
+ headers = %w[Role Host Private\ IP Label Status Conn Lag]
175
+
176
+ if cols[:pgbouncer]
177
+ fmt += " %-#{cols[:pgbouncer]}s"
178
+ headers << 'PgBouncer'
179
+ end
180
+
181
+ header = format(fmt, *headers)
146
182
  puts header
147
183
  puts '-' * header.length
148
184
  end
@@ -150,8 +186,15 @@ module ActivePostgres
150
186
  def print_table_row(node, cols)
151
187
  fmt = "%-#{cols[:role]}s %-#{cols[:host]}s %-#{cols[:private_ip]}s " \
152
188
  "%-#{cols[:label]}s %-#{cols[:status]}s %#{cols[:conn]}d %#{cols[:lag]}s"
153
- puts format(fmt, node[:role], node[:host], node[:private_ip], node[:label],
154
- node[:status], node[:connections], node[:lag])
189
+ values = [node[:role], node[:host], node[:private_ip], node[:label],
190
+ node[:status], node[:connections], node[:lag]]
191
+
192
+ if cols[:pgbouncer]
193
+ fmt += " %-#{cols[:pgbouncer]}s"
194
+ values << node[:pgbouncer]
195
+ end
196
+
197
+ puts format(fmt, *values)
155
198
  end
156
199
 
157
200
  def print_components
@@ -240,5 +283,37 @@ module ActivePostgres
240
283
  mb = kb / 1024.0
241
284
  "#{mb.round(1)} MB"
242
285
  end
286
+
287
+ def check_pgbouncer_status(host)
288
+ return '-' unless config.component_enabled?(:pgbouncer)
289
+
290
+ ssh_executor.execute_on_host(host) do
291
+ result = capture(:sudo, 'systemctl', 'is-active', 'pgbouncer').strip
292
+ result == 'active' ? '✓ running' : '✗ down'
293
+ end
294
+ rescue StandardError
295
+ '✗ down'
296
+ end
297
+
298
+ def check_pgbouncer_running(host)
299
+ ssh_executor.execute_on_host(host) do
300
+ result = capture(:sudo, 'systemctl', 'is-active', 'pgbouncer').strip
301
+ result == 'active'
302
+ end
303
+ rescue StandardError
304
+ false
305
+ end
306
+
307
+ def check_pgbouncer_userlist(host)
308
+ app_user = config.app_user
309
+ return true unless app_user # No app user configured, skip check
310
+
311
+ ssh_executor.execute_on_host(host) do
312
+ userlist = capture(:sudo, 'cat', '/etc/pgbouncer/userlist.txt').strip
313
+ userlist.include?(app_user)
314
+ end
315
+ rescue StandardError
316
+ false
317
+ end
243
318
  end
244
319
  end
@@ -280,7 +280,9 @@ module ActivePostgres
280
280
  keys_only: true,
281
281
  forward_agent: false,
282
282
  auth_methods: ['publickey'],
283
- verify_host_key: :never
283
+ verify_host_key: :accept_new,
284
+ timeout: 10,
285
+ number_of_password_prompts: 0
284
286
  }
285
287
  end
286
288
  end
@@ -95,7 +95,7 @@ module ActivePostgres
95
95
  def deploy_pgbouncer
96
96
  logger.task('Setting up pgbouncer on standby') do
97
97
  component = Components::PgBouncer.new(config, ssh_executor, secrets)
98
- component.install_on_standby(standby_host) if component.respond_to?(:install_on_standby)
98
+ component.install_on_standby(standby_host)
99
99
  end
100
100
  end
101
101
 
@@ -1,3 +1,3 @@
1
1
  module ActivePostgres
2
- VERSION = '0.4.0'.freeze
2
+ VERSION = '0.6.0'.freeze
3
3
  end
@@ -336,6 +336,24 @@ namespace :postgres do
336
336
  end
337
337
 
338
338
  namespace :setup do
339
+ desc 'Setup only core PostgreSQL (updates postgresql.conf and pg_hba.conf)'
340
+ task core: :environment do
341
+ require 'active_postgres'
342
+
343
+ config = ActivePostgres::Configuration.load
344
+ installer = ActivePostgres::Installer.new(config)
345
+ installer.setup_component('core')
346
+ end
347
+
348
+ desc 'Setup only SSL certificates'
349
+ task ssl: :environment do
350
+ require 'active_postgres'
351
+
352
+ config = ActivePostgres::Configuration.load
353
+ installer = ActivePostgres::Installer.new(config)
354
+ installer.setup_component('ssl')
355
+ end
356
+
339
357
  desc 'Setup only PgBouncer'
340
358
  task pgbouncer: :environment do
341
359
  require 'active_postgres'
@@ -508,6 +526,18 @@ namespace :postgres do
508
526
  key_valid = test('[ -f /etc/postgresql/*/main/server.key ]')
509
527
  if cert_valid && key_valid
510
528
  info ' Certificates: Present ✅'
529
+ cert_issuer = begin
530
+ capture(:sudo, 'openssl', 'x509', '-in', "/etc/postgresql/#{config.version}/main/server.crt",
531
+ '-noout', '-issuer', '2>/dev/null').strip
532
+ rescue StandardError
533
+ nil
534
+ end
535
+ if cert_issuer
536
+ issuer_o = cert_issuer.match(/O\s*=\s*"?([^",\/]+)"?/)&.captures&.first
537
+ issuer_cn = cert_issuer.match(/CN\s*=\s*([^,\/]+)/)&.captures&.first
538
+ issuer_name = issuer_o || issuer_cn || cert_issuer.sub('issuer=', '')
539
+ info " Issuer: #{issuer_name.strip}"
540
+ end
511
541
  results[:passed] << "#{label}: SSL enabled with certificates"
512
542
  else
513
543
  warn ' Certificates: Missing ⚠️'
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.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - BoringCache
@@ -172,6 +172,8 @@ files:
172
172
  - lib/active_postgres/error_handler.rb
173
173
  - lib/active_postgres/failover.rb
174
174
  - lib/active_postgres/generators/active_postgres/install_generator.rb
175
+ - lib/active_postgres/generators/active_postgres/templates/database.active_postgres.yml.erb
176
+ - lib/active_postgres/generators/active_postgres/templates/postgres.yml.erb
175
177
  - lib/active_postgres/health_checker.rb
176
178
  - lib/active_postgres/installer.rb
177
179
  - lib/active_postgres/log_sanitizer.rb
@@ -218,7 +220,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
218
220
  - !ruby/object:Gem::Version
219
221
  version: '0'
220
222
  requirements: []
221
- rubygems_version: 3.6.7
223
+ rubygems_version: 3.7.2
222
224
  specification_version: 4
223
225
  summary: PostgreSQL High Availability for Rails, made simple
224
226
  test_files: []