potluck-postgres 0.0.5 → 0.0.6

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +1 -1
  3. data/lib/potluck/postgres.rb +99 -57
  4. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 667d6b357e4b021bba60a415b90f8623d9b72683ed3b5869ce87f2e43e033325
4
- data.tar.gz: 656fd69e5541b414c9e67f750daabaf072c94fbf1237042424a3a5848cfedfa8
3
+ metadata.gz: e2dc9b254c8fd0d0bd4c7fee8d03705c1fe054577addaacf874286bfb32d0a2a
4
+ data.tar.gz: b995b1b64aa3bca01392fcf874eaf71a7fae58bf0a5d0da54cc1e42269ed0499
5
5
  SHA512:
6
- metadata.gz: 03b852e0ce06ed33d0c7010779eb5bd6d1804e0151df4814a3ad218a50ffcaa27daebf47e7b7288ac7755c75ef4c11b003a82516f6a383e724dbbcdc4269759c
7
- data.tar.gz: c3cfc79e9c846ca36e2da1015871cac23928bca1291bb7ee7c05ec8b459c71c4918bf057f70516da5dec9a6ab6942a4fc9d1428af970ab79bbfb20936039f5d9
6
+ metadata.gz: 734fc97cfe75c7c0af39b2fb65db31dc6fe7d8355995fd339c7ec3808ffb2ee2f01de2b4852753b8ad45acde92a508126eef475725db2327c2482721c0a3b1d2
7
+ data.tar.gz: 108749ea87986e3af08ef7f392b381880a5b391f44a3e86c237620c52a60c67bd591c8784d59a27c8b75ac88e3016ebb4987d0bb33054b28d7712312d705f357
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2021 Nate Pickens
1
+ Copyright 2021-2022 Nate Pickens
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4
4
  documentation files (the "Software"), to deal in the Software without restriction, including without
@@ -64,34 +64,45 @@ module Potluck
64
64
  # Connects to the configured Postgres database.
65
65
  #
66
66
  def connect
67
- (tries ||= 0) && (tries += 1)
68
- @database = Sequel.connect(@config, logger: @logger)
69
- rescue Sequel::DatabaseConnectionError => e
70
- if (dud = Sequel::DATABASES.last)
71
- dud.disconnect
72
- Sequel.synchronize { Sequel::DATABASES.delete(dud) }
73
- end
67
+ role_created = false
68
+ database_created = false
69
+
70
+ begin
71
+ (tries ||= 0) && (tries += 1)
72
+ @database = Sequel.connect(@config, logger: @logger)
73
+ rescue Sequel::DatabaseConnectionError => e
74
+ if (dud = Sequel::DATABASES.last)
75
+ dud.disconnect
76
+ Sequel.synchronize { Sequel::DATABASES.delete(dud) }
77
+ end
74
78
 
75
- message = e.message.downcase
76
-
77
- if message =~ ROLE_NOT_FOUND_REGEX && tries == 1
78
- create_database_role
79
- create_database
80
- retry
81
- elsif message =~ DATABASE_NOT_FOUND_REGEX && tries == 1
82
- create_database
83
- retry
84
- elsif message.include?(STARTING_UP_STRING) && tries < STARTING_UP_TIMEOUT
85
- sleep(1)
86
- retry
87
- elsif message.include?(CONNECTION_REFUSED_STRING) && tries < CONNECTION_REFUSED_TIMEOUT && manage?
88
- sleep(1)
89
- retry
90
- elsif message.include?(CONNECTION_REFUSED_STRING)
91
- raise(PostgresError.new(e.message.strip, e))
92
- else
93
- raise
79
+ message = e.message.downcase
80
+
81
+ if message =~ ROLE_NOT_FOUND_REGEX && !role_created && manage?
82
+ role_created = true
83
+ create_role
84
+ retry
85
+ elsif message =~ DATABASE_NOT_FOUND_REGEX && !database_created && manage?
86
+ database_created = true
87
+ create_database
88
+ retry
89
+ elsif message.include?(STARTING_UP_STRING) && tries < STARTING_UP_TIMEOUT
90
+ sleep(1)
91
+ retry
92
+ elsif message.include?(CONNECTION_REFUSED_STRING) && tries < CONNECTION_REFUSED_TIMEOUT
93
+ sleep(1)
94
+ retry
95
+ elsif message.include?(CONNECTION_REFUSED_STRING)
96
+ raise(PostgresError.new(e.message.strip, e))
97
+ else
98
+ raise
99
+ end
94
100
  end
101
+
102
+ # Only grant permissions if the database already existed but the role did not. Automatic database
103
+ # creation (via #create_database) is performed as the configured role, which means explicit permission
104
+ # granting is not necessary.
105
+ grant_permissions if role_created && !database_created
95
106
  end
96
107
 
97
108
  ##
@@ -118,7 +129,7 @@ module Potluck
118
129
  original_level = @logger.level
119
130
  @logger.level = Logger::WARN if @logger.level == Logger::INFO
120
131
 
121
- args = [Sequel::Model.db, dir, {allow_missing_migration_files: true}]
132
+ args = [@database, dir, {allow_missing_migration_files: true}]
122
133
  migrator = Sequel::TimestampMigrator.new(*args)
123
134
 
124
135
  return if migrator.files.empty?
@@ -143,26 +154,47 @@ module Potluck
143
154
  @logger.level = original_level if original_level
144
155
  end
145
156
 
157
+ ##
158
+ # Content of the launchctl plist file.
159
+ #
160
+ def self.plist
161
+ super(
162
+ <<~EOS
163
+ <key>ProgramArguments</key>
164
+ <array>
165
+ <string>/usr/local/opt/postgresql/bin/postgres</string>
166
+ <string>-D</string>
167
+ <string>/usr/local/var/postgres</string>
168
+ </array>
169
+ <key>WorkingDirectory</key>
170
+ <string>/usr/local</string>
171
+ <key>StandardOutPath</key>
172
+ <string>/usr/local/var/log/postgres.log</string>
173
+ <key>StandardErrorPath</key>
174
+ <string>/usr/local/var/log/postgres.log</string>
175
+ EOS
176
+ )
177
+ end
178
+
146
179
  private
147
180
 
148
181
  ##
149
182
  # Attempts to connect to the 'postgres' database as the system user with no password and create the
150
183
  # configured role. Useful in development.
151
184
  #
152
- def create_database_role
153
- tmp_config = @config.dup
185
+ def create_role
186
+ tmp_config = admin_database_config
154
187
  tmp_config[:database] = 'postgres'
155
- tmp_config[:username] = ENV['USER']
156
- tmp_config[:password] = nil
157
188
 
158
189
  begin
159
190
  Sequel.connect(tmp_config, logger: @logger) do |database|
160
- database.execute("CREATE ROLE #{@config[:username]} WITH LOGIN CREATEDB REPLICATION PASSWORD "\
161
- "'#{@config[:password]}'")
191
+ database.execute("CREATE ROLE \"#{@config[:username]}\" WITH LOGIN CREATEDB REPLICATION"\
192
+ "#{" PASSWORD '#{@config[:password]}'" if @config[:password]}")
162
193
  end
163
194
  rescue => e
164
- raise(PostgresError.new("Database role #{@config[:username].inspect} could not be created using "\
165
- "system user #{tmp_config[:username].inspect}. Please create the role manually.", e))
195
+ raise(PostgresError.new("Failed to create database role #{@config[:username].inspect} by "\
196
+ "connecting to database #{tmp_config[:database].inspect} as role "\
197
+ "#{tmp_config[:username].inspect}. Please create the role manually.", e))
166
198
  end
167
199
  end
168
200
 
@@ -176,35 +208,45 @@ module Potluck
176
208
 
177
209
  begin
178
210
  Sequel.connect(tmp_config, logger: @logger) do |database|
179
- database.execute("CREATE DATABASE #{@config[:database]}")
211
+ database.execute("CREATE DATABASE \"#{@config[:database]}\"")
180
212
  end
181
213
  rescue => e
182
- raise(PostgresError.new("Database #{@config[:database].inspect} could not be created by "\
183
- "connecting to system database #{tmp_config[:database].inspect}. Please create the database "\
184
- 'manually.', e))
214
+ raise(PostgresError.new("Failed to create database #{@config[:database].inspect} by connecting to "\
215
+ "database #{tmp_config[:database].inspect} as role #{tmp_config[:username].inspect}. "\
216
+ 'Please create the database manually.', e))
185
217
  end
186
218
  end
187
219
 
188
220
  ##
189
- # Content of the launchctl plist file.
221
+ # Grants appropriate permissions for the configured database role.
190
222
  #
191
- def self.plist
192
- super(
193
- <<~EOS
194
- <key>ProgramArguments</key>
195
- <array>
196
- <string>/usr/local/opt/postgresql/bin/postgres</string>
197
- <string>-D</string>
198
- <string>/usr/local/var/postgres</string>
199
- </array>
200
- <key>WorkingDirectory</key>
201
- <string>/usr/local</string>
202
- <key>StandardOutPath</key>
203
- <string>/usr/local/var/log/postgres.log</string>
204
- <key>StandardErrorPath</key>
205
- <string>/usr/local/var/log/postgres.log</string>
206
- EOS
207
- )
223
+ def grant_permissions
224
+ tmp_config = admin_database_config
225
+
226
+ begin
227
+ Sequel.connect(tmp_config, logger: @logger) do |db|
228
+ db.execute("GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"#{@config[:username]}\"")
229
+ db.execute("GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO \"#{@config[:username]}\"")
230
+ db.execute("ALTER DEFAULT PRIVILEGES FOR ROLE \"#{@config[:username]}\" IN SCHEMA public GRANT "\
231
+ "ALL PRIVILEGES ON TABLES TO \"#{@config[:username]}\"")
232
+ end
233
+ rescue => e
234
+ raise(PostgresError.new("Failed to grant database permissions for role "\
235
+ "#{@config[:username].inspect} by connecting as role #{tmp_config[:username].inspect}. Please "\
236
+ 'grant appropriate permissions manually.', e))
237
+ end
238
+ end
239
+
240
+ ##
241
+ # Returns a configuration hash for connecting to Postgres to perform administrative tasks (i.e. role and
242
+ # database creation). Uses the system user as the username and no password.
243
+ #
244
+ def admin_database_config
245
+ config = @config.dup
246
+ config[:username] = ENV['USER']
247
+ config[:password] = nil
248
+
249
+ config
208
250
  end
209
251
  end
210
252
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: potluck-postgres
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Pickens
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-31 00:00:00.000000000 Z
11
+ date: 2022-01-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: potluck
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.5
19
+ version: 0.0.6
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.5
26
+ version: 0.0.6
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement