pg_easy_replicate 0.1.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3e58d75f6e22242f3b833838eba69bfd72cb930ad5931ef77a5c0c4f49aded8
4
- data.tar.gz: 8ff3c66b8ee6b37bcadb801f57fa668019fc7c36c5cabf5639c65d3b80813eae
3
+ metadata.gz: f655d3bf7d458de6b84a85930e7175c05f780a390e9431531a0ae30c35d4737b
4
+ data.tar.gz: fa23c33715823598d4795ba64cbf466088d01fd97315bfe97d3a05db2da50f48
5
5
  SHA512:
6
- metadata.gz: d337088ee7ccf6855c45328502c587172cd96fb4b57bb386a6774665277d5753533f7876fadc28d6088ab0ecfb4cc9e201245d8a05f83ca178f9dd6376df4a00
7
- data.tar.gz: 8d44166f5ad25773462e952109b315195229e1127639296e3f4f56341823af60fd7feba3bef1ffcbd32cbb864cab28117bbedda3ceb9949aa96451296bc63a78
6
+ metadata.gz: 1e69d89ca00ca077784aab0c042e46c85faf7e15f2e585a65321084c5b3543502a7c9a440faa17f829848015a31ed50393129a0d7bfa083a27a731af4c179b68
7
+ data.tar.gz: ce29729ddcf74420818c0b4d86b98de28fbefd0690368ee4fcdd40f689dcf1ba6c3135850ab1e22de2bbd977c733ca5449cf42ccef728fda20c54b102b38f965
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [0.1.4] - 2023-06-22
2
+
3
+ - Drop lockbox dependency
4
+ - Support password with special chars and test for url encoded URI
5
+ - Support AWS and GCP special user scenarios and introduce `--special-user-role`
6
+
7
+ ## [0.1.3] - 2023-06-22
8
+
9
+ - Docker multi-platform image build support for linux/amd64 and linux/arm64 starting 0.1.3
10
+
1
11
  ## [0.1.2] - 2023-06-22
2
12
 
3
13
  - Keep the internal username unique
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pg_easy_replicate (0.1.3)
5
- lockbox (~> 1.2.0)
4
+ pg_easy_replicate (0.1.5)
6
5
  ougai (~> 2.0.0)
7
6
  pg (~> 1.5.3)
8
7
  sequel (~> 5.69.0)
@@ -19,7 +18,6 @@ GEM
19
18
  thor
20
19
  tilt
21
20
  json (2.6.3)
22
- lockbox (1.2.0)
23
21
  method_source (1.0.0)
24
22
  oj (3.14.3)
25
23
  ougai (2.0.0)
data/README.md CHANGED
@@ -15,6 +15,9 @@ Battle tested in production at [Tines](https://www.tines.com/) 🚀
15
15
  - [Replicating all tables with a single group](#replicating-all-tables-with-a-single-group)
16
16
  - [Config check](#config-check)
17
17
  - [Bootstrap](#bootstrap)
18
+ - [Bootstrap and Config Check with special user role in AWS or GCP](#bootstrap-and-config-check-with-special-user-role-in-aws-or-gcp)
19
+ - [Config Check](#config-check)
20
+ - [Bootstrap](#bootstrap-1)
18
21
  - [Start sync](#start-sync)
19
22
  - [Stats](#stats)
20
23
  - [Performing switchover](#performing-switchover)
@@ -22,6 +25,7 @@ Battle tested in production at [Tines](https://www.tines.com/) 🚀
22
25
  - [Switchover strategies with minimal downtime](#switchover-strategies-with-minimal-downtime)
23
26
  - [Rolling restart strategy](#rolling-restart-strategy)
24
27
  - [DNS Failover strategy](#dns-failover-strategy)
28
+ - [Contributing](#contributing)
25
29
 
26
30
  ## Installation
27
31
 
@@ -51,7 +55,7 @@ https://hub.docker.com/r/shayonj/pg_easy_replicate
51
55
 
52
56
  - PostgreSQL 10 and later
53
57
  - Ruby 2.7 and later
54
- - Database user should have permissions for `SUPERUSER`
58
+ - Database user should have permissions for `SUPERUSER` or pass in the special user role that has the privileges to create role, schema, publication and subscription on both databases. More on `--special-user-role` section below.
55
59
  - Both databases should have the same schema
56
60
 
57
61
  ## Limits
@@ -116,6 +120,31 @@ $ pg_easy_replicate bootstrap --group-name database-cluster-1
116
120
  ...
117
121
  ```
118
122
 
123
+ ### Bootstrap and Config Check with special user role in AWS or GCP
124
+
125
+ If you don't want your primary login user to have `superuser` privileges or you are on AWS or GCP, you will need to pass in the special user role that has the privileges to create role, schema, publication and subscription. This is required so `pg_easy_replicate` can create a dedicated user for replication which is granted the respective special user role to carry out its functionalities.
126
+
127
+ For AWS the special user role is `rds_superuser`, and for GCP it is `cloudsqlsuperuser`. Please refer to docs for the most up to date information.
128
+
129
+ **Note**: The user in the connection url must be part of the special user role being supplied.
130
+
131
+ #### Config Check
132
+
133
+ ```bash
134
+ $ pg_easy_replicate config_check --special-user-role="rds_superuser"
135
+
136
+ ✅ Config is looking good.
137
+ ```
138
+
139
+ #### Bootstrap
140
+
141
+ ```bash
142
+ $ pg_easy_replicate bootstrap --group-name database-cluster-1 --special-user-role="rds_superuser"
143
+
144
+ {"name":"pg_easy_replicate","hostname":"PKHXQVK6DW","pid":21485,"level":30,"time":"2023-06-19T15:51:11.015-04:00","v":0,"msg":"Setting up schema","version":"0.1.0"}
145
+ ...
146
+ ```
147
+
119
148
  ### Start sync
120
149
 
121
150
  Once the bootstrap is complete, you can start the sync. Starting the sync sets up the publication, subscription and performs other minor housekeeping things.
data/bin/release.sh CHANGED
@@ -1,3 +1,7 @@
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
1
5
  export VERSION=$1
2
6
  echo "VERSION: ${VERSION}"
3
7
 
@@ -7,12 +11,12 @@ gem build pg_easy_replicate.gemspec
7
11
  echo "=== Pushing gem ===="
8
12
  gem push pg_easy_replicate-"$VERSION".gem
9
13
 
10
- echo "=== Sleeping for 5s ===="
11
- sleep 5
14
+ echo "=== Sleeping for 15s ===="
15
+ sleep 15
12
16
 
13
- # echo "=== Pushing tags to github ===="
14
- # git tag v"$VERSION"
15
- # git push origin --tags
17
+ echo "=== Pushing tags to github ===="
18
+ git tag v"$VERSION"
19
+ git push origin --tags
16
20
 
17
- # echo "=== Cleaning up ===="
18
- # rm pg_easy_replicate-"$VERSION".gem
21
+ echo "=== Cleaning up ===="
22
+ rm pg_easy_replicate-"$VERSION".gem
data/bin/test.sh ADDED
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+
3
+ set -euo pipefail
4
+
5
+ export SECONDARY_SOURCE_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@source_db/postgres"
6
+ export SOURCE_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@localhost:5432/postgres"
7
+ export TARGET_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@localhost:5433/postgres"
8
+
9
+ bundle exec bin/pg_easy_replicate config_check
10
+
11
+ # Bootstrap and cleanup
12
+ echo "===== Performing Bootstrap and cleanup"
13
+ bundle exec bin/pg_easy_replicate bootstrap -g cluster-1
14
+ bundle exec bin/pg_easy_replicate cleanup -e -g cluster-1
15
+
16
+ # Bootstrap and start_sync
17
+ echo "===== Performing Bootstrap, start_sync, stop_sync and cleanup"
18
+ bundle exec bin/pg_easy_replicate bootstrap -g cluster-1
19
+ bundle exec bin/pg_easy_replicate start_sync -g cluster-1
20
+ bundle exec bin/pg_easy_replicate stop_sync -g cluster-1
21
+ bundle exec bin/pg_easy_replicate cleanup -e -g cluster-1
22
+
23
+ # Bootstrap with switchover
24
+ echo "===== Performing Bootstrap, start_sync, stop_sync and cleanup"
25
+ bundle exec bin/pg_easy_replicate bootstrap -g cluster-1
26
+ bundle exec bin/pg_easy_replicate start_sync -g cluster-1
27
+ # bundle exec bin/pg_easy_replicate switchover -g cluster-1
28
+ bundle exec bin/pg_easy_replicate cleanup -e -g cluster-1
data/docker-compose.yml CHANGED
@@ -6,7 +6,7 @@ services:
6
6
  - "5432:5432"
7
7
  environment:
8
8
  POSTGRES_USER: jamesbond
9
- POSTGRES_PASSWORD: jamesbond
9
+ POSTGRES_PASSWORD: jamesbond123@7!'3aaR
10
10
  POSTGRES_DB: postgres
11
11
  command: >
12
12
  -c wal_level=logical
@@ -22,7 +22,7 @@ services:
22
22
  - "5433:5432"
23
23
  environment:
24
24
  POSTGRES_USER: jamesbond
25
- POSTGRES_PASSWORD: jamesbond
25
+ POSTGRES_PASSWORD: jamesbond123@7!'3aaR
26
26
  POSTGRES_DB: postgres
27
27
  command: >
28
28
  -c wal_level=logical
@@ -8,8 +8,14 @@ module PgEasyReplicate
8
8
 
9
9
  desc "config_check",
10
10
  "Prints if source and target database have the required config"
11
+ method_option :special_user_role,
12
+ aliases: "-s",
13
+ desc:
14
+ "Name of the role that has superuser permissions. Usually useful for AWS (rds_superuser) or GCP (cloudsqlsuperuser)."
11
15
  def config_check
12
- PgEasyReplicate.assert_config
16
+ PgEasyReplicate.assert_config(
17
+ special_user_role: options[:special_user_role],
18
+ )
13
19
 
14
20
  puts "✅ Config is looking good."
15
21
  end
@@ -18,6 +24,10 @@ module PgEasyReplicate
18
24
  aliases: "-g",
19
25
  required: true,
20
26
  desc: "Name of the group to provision"
27
+ method_option :special_user_role,
28
+ aliases: "-s",
29
+ desc:
30
+ "Name of the role that has superuser permissions. Usually useful with AWS (rds_superuser) or GCP (cloudsqlsuperuser)."
21
31
  desc "bootstrap",
22
32
  "Sets up temporary tables for information required during runtime"
23
33
  def bootstrap
@@ -71,7 +81,7 @@ module PgEasyReplicate
71
81
  required: true,
72
82
  desc: "Name of the group previously provisioned"
73
83
  def stop_sync
74
- PgEasyReplicate::Orchestrate.stop_sync(options[:group_name])
84
+ PgEasyReplicate::Orchestrate.stop_sync(group_name: options[:group_name])
75
85
  end
76
86
 
77
87
  desc "switchover ",
@@ -60,6 +60,10 @@ module PgEasyReplicate
60
60
  connection_info(url)[:user]
61
61
  end
62
62
 
63
+ def db_name(url)
64
+ connection_info(url)[:dbname]
65
+ end
66
+
63
67
  def abort_with(msg)
64
68
  raise(msg) if test_env?
65
69
  abort(msg)
@@ -9,8 +9,6 @@ module PgEasyReplicate
9
9
  DEFAULT_WAIT = 5 # seconds
10
10
 
11
11
  def start_sync(options)
12
- PgEasyReplicate.assert_config
13
-
14
12
  create_publication(
15
13
  group_name: options[:group_name],
16
14
  conn_string: source_db_url,
@@ -62,10 +60,14 @@ module PgEasyReplicate
62
60
  "Setting up publication",
63
61
  { publication_name: publication_name(group_name) },
64
62
  )
63
+
65
64
  Query.run(
66
65
  query: "create publication #{publication_name(group_name)}",
67
66
  connection_url: conn_string,
67
+ user: db_user(conn_string),
68
68
  )
69
+ rescue => e
70
+ raise "Unable to create publication: #{e.message}"
69
71
  end
70
72
 
71
73
  def add_tables_to_publication(
@@ -91,6 +93,8 @@ module PgEasyReplicate
91
93
  schema: schema,
92
94
  )
93
95
  end
96
+ rescue => e
97
+ raise "Unable to add tables to publication: #{e.message}"
94
98
  end
95
99
 
96
100
  def list_all_tables(schema:, conn_string:)
@@ -112,7 +116,10 @@ module PgEasyReplicate
112
116
  Query.run(
113
117
  query: "DROP PUBLICATION IF EXISTS #{publication_name(group_name)}",
114
118
  connection_url: conn_string,
119
+ user: db_user(conn_string),
115
120
  )
121
+ rescue => e
122
+ raise "Unable to drop publication: #{e.message}"
116
123
  end
117
124
 
118
125
  def create_subscription(
@@ -132,6 +139,7 @@ module PgEasyReplicate
132
139
  query:
133
140
  "CREATE SUBSCRIPTION #{subscription_name(group_name)} CONNECTION '#{source_conn_string}' PUBLICATION #{publication_name(group_name)}",
134
141
  connection_url: target_conn_string,
142
+ user: db_user(target_conn_string),
135
143
  transaction: false,
136
144
  )
137
145
  rescue Sequel::DatabaseError => e
@@ -141,7 +149,7 @@ module PgEasyReplicate
141
149
  )
142
150
  end
143
151
 
144
- raise
152
+ raise "Unable to create subscription: #{e.message}"
145
153
  end
146
154
 
147
155
  def drop_subscription(group_name:, target_conn_string:)
@@ -157,11 +165,15 @@ module PgEasyReplicate
157
165
  connection_url: target_conn_string,
158
166
  transaction: false,
159
167
  )
168
+ rescue => e
169
+ raise "Unable to drop subscription: #{e.message}"
160
170
  end
161
171
 
162
- def stop_sync(target_conn_string:, source_conn_string:, group_name:)
163
- PgEasyReplicate.assert_config
164
-
172
+ def stop_sync(
173
+ group_name:,
174
+ source_conn_string: source_db_url,
175
+ target_conn_string: target_db_url
176
+ )
165
177
  logger.info(
166
178
  "Stopping sync",
167
179
  {
@@ -177,6 +189,8 @@ module PgEasyReplicate
177
189
  group_name: group_name,
178
190
  target_conn_string: target_conn_string,
179
191
  )
192
+ rescue => e
193
+ raise "Unable to stop sync user: #{e.message}"
180
194
  end
181
195
 
182
196
  def switchover(
@@ -185,7 +199,6 @@ module PgEasyReplicate
185
199
  target_conn_string: target_db_url,
186
200
  lag_delta_size: DEFAULT_LAG
187
201
  )
188
- PgEasyReplicate.assert_config
189
202
  group = Group.find(group_name)
190
203
 
191
204
  watch_lag(group_name: group_name, lag: lag_delta_size)
@@ -258,6 +271,8 @@ module PgEasyReplicate
258
271
  "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE usename = '#{db_user(source_db_url)}';"
259
272
 
260
273
  Query.run(query: kill_sql, connection_url: source_db_url)
274
+ rescue => e
275
+ raise "Unable to revoke connections on source db: #{e.message}"
261
276
  end
262
277
 
263
278
  def restore_connections_on_source_db(group_name)
@@ -298,6 +313,8 @@ module PgEasyReplicate
298
313
  SQL
299
314
 
300
315
  Query.run(query: sql, connection_url: conn_string, schema: schema)
316
+ rescue => e
317
+ raise "Unable to refresh sequences: #{e.message}"
301
318
  end
302
319
 
303
320
  def mark_switchover_complete(group_name)
@@ -13,7 +13,6 @@ module PgEasyReplicate
13
13
 
14
14
  class << self
15
15
  def object(group_name)
16
- PgEasyReplicate.assert_config
17
16
  stats = replication_stats(group_name)
18
17
  group = Group.find(group_name)
19
18
  {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgEasyReplicate
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.5"
5
5
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "json"
4
4
  require "ougai"
5
- require "lockbox"
6
5
  require "pg"
7
6
  require "sequel"
8
7
 
@@ -22,17 +21,20 @@ module PgEasyReplicate
22
21
  extend Helper
23
22
 
24
23
  class << self
25
- def config
24
+ def config(special_user_role: nil)
26
25
  abort_with("SOURCE_DB_URL is missing") if source_db_url.nil?
27
26
  abort_with("TARGET_DB_URL is missing") if target_db_url.nil?
27
+
28
28
  @config ||=
29
29
  begin
30
30
  q =
31
31
  "select name, setting from pg_settings where name in ('max_wal_senders', 'max_worker_processes', 'wal_level', 'max_replication_slots', 'max_logical_replication_workers');"
32
32
 
33
33
  {
34
- source_db_is_superuser: is_super_user?(source_db_url),
35
- target_db_is_superuser: is_super_user?(target_db_url),
34
+ source_db_is_super_user:
35
+ is_super_user?(source_db_url, special_user_role),
36
+ target_db_is_super_user:
37
+ is_super_user?(target_db_url, special_user_role),
36
38
  source_db:
37
39
  Query.run(
38
40
  query: q,
@@ -51,33 +53,43 @@ module PgEasyReplicate
51
53
  end
52
54
  end
53
55
 
54
- def assert_config
55
- unless assert_wal_level_logical(config.dig(:source_db))
56
+ def assert_config(special_user_role: nil)
57
+ config_hash = config(special_user_role: special_user_role)
58
+
59
+ unless assert_wal_level_logical(config_hash.dig(:source_db))
56
60
  abort_with("WAL_LEVEL should be LOGICAL on source DB")
57
61
  end
58
62
 
59
- unless assert_wal_level_logical(config.dig(:target_db))
63
+ unless assert_wal_level_logical(config_hash.dig(:target_db))
60
64
  abort_with("WAL_LEVEL should be LOGICAL on target DB")
61
65
  end
62
66
 
63
- unless config.dig(:source_db_is_superuser)
64
- abort_with("User on source database should be a superuser")
67
+ unless config_hash.dig(:source_db_is_super_user)
68
+ abort_with("User on source database does not have super user privilege")
65
69
  end
66
70
 
67
- return if config.dig(:target_db_is_superuser)
68
- abort_with("User on target database should be a superuser")
71
+ return if config_hash.dig(:target_db_is_super_user)
72
+ abort_with("User on target database does not have super user privilege")
69
73
  end
70
74
 
71
75
  def bootstrap(options)
72
- assert_config
73
76
  logger.info("Setting up schema")
74
77
  setup_schema
75
78
 
76
79
  logger.info("Setting up replication user on source database")
77
- create_user(conn_string: source_db_url, group_name: options[:group_name])
80
+ create_user(
81
+ conn_string: source_db_url,
82
+ group_name: options[:group_name],
83
+ special_user_role: options[:special_user_role],
84
+ grant_permissions_on_schema: true,
85
+ )
78
86
 
79
87
  logger.info("Setting up replication user on target database")
80
- create_user(conn_string: target_db_url, group_name: options[:group_name])
88
+ create_user(
89
+ conn_string: target_db_url,
90
+ group_name: options[:group_name],
91
+ special_user_role: options[:special_user_role],
92
+ )
81
93
 
82
94
  logger.info("Setting up groups tables")
83
95
  Group.setup
@@ -123,7 +135,10 @@ module PgEasyReplicate
123
135
  query: "DROP SCHEMA IF EXISTS #{internal_schema_name} CASCADE",
124
136
  connection_url: source_db_url,
125
137
  schema: internal_schema_name,
138
+ user: db_user(target_db_url),
126
139
  )
140
+ rescue => e
141
+ raise "Unable to drop schema: #{e.message}"
127
142
  end
128
143
 
129
144
  def setup_schema
@@ -139,6 +154,8 @@ module PgEasyReplicate
139
154
  schema: internal_schema_name,
140
155
  user: db_user(target_db_url),
141
156
  )
157
+ rescue => e
158
+ raise "Unable to setup schema: #{e.message}"
142
159
  end
143
160
 
144
161
  def logger
@@ -160,38 +177,109 @@ module PgEasyReplicate
160
177
  end
161
178
  end
162
179
 
163
- def is_super_user?(url)
164
- Query.run(
165
- query:
166
- "select usesuper from pg_user where usename = '#{db_user(url)}';",
167
- connection_url: url,
168
- user: db_user(target_db_url),
169
- ).first[
170
- :usesuper
171
- ]
180
+ def is_super_user?(url, special_user_role = nil)
181
+ if special_user_role
182
+ sql = <<~SQL
183
+ SELECT r.rolname AS username,
184
+ r1.rolname AS "role"
185
+ FROM pg_catalog.pg_roles r
186
+ LEFT JOIN pg_catalog.pg_auth_members m ON (m.member = r.oid)
187
+ LEFT JOIN pg_roles r1 ON (m.roleid=r1.oid)
188
+ WHERE r.rolname = '#{db_user(url)}'
189
+ ORDER BY 1;
190
+ SQL
191
+
192
+ r =
193
+ Query.run(
194
+ query: sql,
195
+ connection_url: url,
196
+ user: db_user(target_db_url),
197
+ )
198
+ # If special_user_role is passed just ensure the url in conn_string has been granted
199
+ # the special_user_role
200
+ r.any? { |q| q[:role] == special_user_role }
201
+ else
202
+ r =
203
+ Query.run(
204
+ query:
205
+ "SELECT rolname, rolsuper FROM pg_roles where rolname = '#{db_user(url)}';",
206
+ connection_url: url,
207
+ user: db_user(target_db_url),
208
+ )
209
+ r.any? { |q| q[:rolsuper] }
210
+ end
211
+ rescue => e
212
+ raise "Unable to check superuser conditions: #{e.message}"
172
213
  end
173
214
 
174
- def create_user(conn_string:, group_name:)
175
- password = connection_info(conn_string)[:user]
215
+ def create_user(
216
+ conn_string:,
217
+ group_name:,
218
+ special_user_role: nil,
219
+ grant_permissions_on_schema: false
220
+ )
221
+ password = connection_info(conn_string)[:password].gsub("'") { "''" }
222
+
176
223
  sql = <<~SQL
177
224
  drop role if exists #{internal_user_name};
178
- create role #{internal_user_name} with password '#{password}' login superuser createdb createrole;
225
+ create role #{internal_user_name} with password '#{password}' login createdb createrole;
226
+ grant all privileges on database #{db_name(conn_string)} TO #{internal_user_name};
179
227
  SQL
180
228
 
181
229
  Query.run(
182
230
  query: sql,
183
231
  connection_url: conn_string,
184
- user: db_user(target_db_url),
232
+ user: db_user(conn_string),
233
+ transaction: false,
234
+ )
235
+
236
+ sql =
237
+ if special_user_role
238
+ "grant #{special_user_role} to #{internal_user_name};"
239
+ else
240
+ "alter user #{internal_user_name} with superuser;"
241
+ end
242
+
243
+ Query.run(
244
+ query: sql,
245
+ connection_url: conn_string,
246
+ user: db_user(conn_string),
247
+ transaction: false,
185
248
  )
249
+
250
+ return unless grant_permissions_on_schema
251
+ Query.run(
252
+ query:
253
+ "grant all on schema #{internal_schema_name} to #{internal_user_name}",
254
+ connection_url: conn_string,
255
+ user: db_user(conn_string),
256
+ transaction: false,
257
+ )
258
+ rescue => e
259
+ raise "Unable to create user: #{e.message}"
186
260
  end
187
261
 
188
262
  def drop_user(conn_string:, group_name:)
189
- sql = "drop role if exists #{internal_user_name};"
263
+ sql = <<~SQL
264
+ revoke all privileges on database #{db_name(conn_string)} from #{internal_user_name};
265
+ SQL
190
266
  Query.run(
191
267
  query: sql,
192
268
  connection_url: conn_string,
193
269
  user: db_user(conn_string),
194
270
  )
271
+
272
+ sql = <<~SQL
273
+ drop role if exists #{internal_user_name};
274
+ SQL
275
+
276
+ Query.run(
277
+ query: sql,
278
+ connection_url: conn_string,
279
+ user: db_user(conn_string),
280
+ )
281
+ rescue => e
282
+ raise "Unable to drop user: #{e.message}"
195
283
  end
196
284
  end
197
285
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_easy_replicate
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shayon Mukherjee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-23 00:00:00.000000000 Z
11
+ date: 2023-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: lockbox
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 1.2.0
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: 1.2.0
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: ougai
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -256,6 +242,7 @@ executables:
256
242
  - pg_easy_replicate
257
243
  - pg_easy_replicate_console
258
244
  - release.sh
245
+ - test.sh
259
246
  extensions: []
260
247
  extra_rdoc_files: []
261
248
  files:
@@ -274,6 +261,7 @@ files:
274
261
  - bin/pg_easy_replicate
275
262
  - bin/pg_easy_replicate_console
276
263
  - bin/release.sh
264
+ - bin/test.sh
277
265
  - docker-compose.yml
278
266
  - lib/pg_easy_replicate.rb
279
267
  - lib/pg_easy_replicate/cli.rb