potluck-postgres 0.0.5 → 0.0.7

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: 667d6b357e4b021bba60a415b90f8623d9b72683ed3b5869ce87f2e43e033325
4
- data.tar.gz: 656fd69e5541b414c9e67f750daabaf072c94fbf1237042424a3a5848cfedfa8
3
+ metadata.gz: 6484b446d1c719baef9d5aec6fc545d305107581c62335c33e9b30098f050ff7
4
+ data.tar.gz: a9b09e742ea51367296b898ca20c331830203eace33489e28e7820d3fe914cab
5
5
  SHA512:
6
- metadata.gz: 03b852e0ce06ed33d0c7010779eb5bd6d1804e0151df4814a3ad218a50ffcaa27daebf47e7b7288ac7755c75ef4c11b003a82516f6a383e724dbbcdc4269759c
7
- data.tar.gz: c3cfc79e9c846ca36e2da1015871cac23928bca1291bb7ee7c05ec8b459c71c4918bf057f70516da5dec9a6ab6942a4fc9d1428af970ab79bbfb20936039f5d9
6
+ metadata.gz: 9f1433d0ac8702cb655ce8e7dcdea53c1a196e27e6cce9619b7f79405028e6693565bcfda69d6ade2940950661fbc6b84fdb293aeb25a9462b8ae905550e122f
7
+ data.tar.gz: 4e7f3c780602ba4c62021b0611d9adf44b0dadb049c3ee448c20280141e78588ceff79b2876034af53c584a97349a902ab79df30f0b41fd1b1102c923b00d136
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
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.7
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Potluck
4
+ class Postgres < Service
5
+ VERSION = '0.0.7'
6
+ end
7
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require('potluck')
4
4
  require('sequel')
5
+ require_relative('postgres/version')
5
6
 
6
7
  module Potluck
7
8
  ##
@@ -64,34 +65,45 @@ module Potluck
64
65
  # Connects to the configured Postgres database.
65
66
  #
66
67
  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
68
+ role_created = false
69
+ database_created = false
70
+
71
+ begin
72
+ (tries ||= 0) && (tries += 1)
73
+ @database = Sequel.connect(@config, logger: @logger)
74
+ rescue Sequel::DatabaseConnectionError => e
75
+ if (dud = Sequel::DATABASES.last)
76
+ dud.disconnect
77
+ Sequel.synchronize { Sequel::DATABASES.delete(dud) }
78
+ end
74
79
 
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
80
+ message = e.message.downcase
81
+
82
+ if message =~ ROLE_NOT_FOUND_REGEX && !role_created && manage?
83
+ role_created = true
84
+ create_role
85
+ retry
86
+ elsif message =~ DATABASE_NOT_FOUND_REGEX && !database_created && manage?
87
+ database_created = true
88
+ create_database
89
+ retry
90
+ elsif message.include?(STARTING_UP_STRING) && tries < STARTING_UP_TIMEOUT
91
+ sleep(1)
92
+ retry
93
+ elsif message.include?(CONNECTION_REFUSED_STRING) && tries < CONNECTION_REFUSED_TIMEOUT
94
+ sleep(1)
95
+ retry
96
+ elsif message.include?(CONNECTION_REFUSED_STRING)
97
+ raise(PostgresError.new(e.message.strip, e))
98
+ else
99
+ raise
100
+ end
94
101
  end
102
+
103
+ # Only grant permissions if the database already existed but the role did not. Automatic database
104
+ # creation (via #create_database) is performed as the configured role, which means explicit permission
105
+ # granting is not necessary.
106
+ grant_permissions if role_created && !database_created
95
107
  end
96
108
 
97
109
  ##
@@ -118,7 +130,7 @@ module Potluck
118
130
  original_level = @logger.level
119
131
  @logger.level = Logger::WARN if @logger.level == Logger::INFO
120
132
 
121
- args = [Sequel::Model.db, dir, {allow_missing_migration_files: true}]
133
+ args = [@database, dir, {allow_missing_migration_files: true}]
122
134
  migrator = Sequel::TimestampMigrator.new(*args)
123
135
 
124
136
  return if migrator.files.empty?
@@ -130,7 +142,7 @@ module Potluck
130
142
 
131
143
  return if applied.empty? && steps <= 0
132
144
 
133
- index = [[0, (all.index(current) || -1) + steps].max, all.size].min
145
+ index = [[0, (all.index(current) || -1) + steps].max, all.size - 1].min
134
146
  file = all[index]
135
147
 
136
148
  args.last[:target] = migrator.send(:migration_version_from_file, file)
@@ -143,26 +155,60 @@ module Potluck
143
155
  @logger.level = original_level if original_level
144
156
  end
145
157
 
158
+ ##
159
+ # Content of the launchctl plist file.
160
+ #
161
+ def self.plist
162
+ versions = Dir["#{HOMEBREW_PREFIX}/opt/postgresql@*"].sort_by { |path| path.split('@').last.to_f }
163
+ version =
164
+ if versions.empty?
165
+ raise(PostgresError, "No Postgres installation found (try running `brew install postgresql@X`)")
166
+ else
167
+ File.basename(versions.last)
168
+ end
169
+
170
+ super(
171
+ <<~EOS
172
+ <key>EnvironmentVariables</key>
173
+ <dict>
174
+ <key>LC_ALL</key>
175
+ <string>C</string>
176
+ </dict>
177
+ <key>ProgramArguments</key>
178
+ <array>
179
+ <string>#{HOMEBREW_PREFIX}/opt/#{version}/bin/postgres</string>
180
+ <string>-D</string>
181
+ <string>#{HOMEBREW_PREFIX}/var/#{version}</string>
182
+ </array>
183
+ <key>WorkingDirectory</key>
184
+ <string>#{HOMEBREW_PREFIX}</string>
185
+ <key>StandardOutPath</key>
186
+ <string>#{HOMEBREW_PREFIX}/var/log/#{version}.log</string>
187
+ <key>StandardErrorPath</key>
188
+ <string>#{HOMEBREW_PREFIX}/var/log/#{version}.log</string>
189
+ EOS
190
+ )
191
+ end
192
+
146
193
  private
147
194
 
148
195
  ##
149
196
  # Attempts to connect to the 'postgres' database as the system user with no password and create the
150
197
  # configured role. Useful in development.
151
198
  #
152
- def create_database_role
153
- tmp_config = @config.dup
199
+ def create_role
200
+ tmp_config = admin_database_config
154
201
  tmp_config[:database] = 'postgres'
155
- tmp_config[:username] = ENV['USER']
156
- tmp_config[:password] = nil
157
202
 
158
203
  begin
159
204
  Sequel.connect(tmp_config, logger: @logger) do |database|
160
- database.execute("CREATE ROLE #{@config[:username]} WITH LOGIN CREATEDB REPLICATION PASSWORD "\
161
- "'#{@config[:password]}'")
205
+ database.execute("CREATE ROLE \"#{@config[:username]}\" WITH LOGIN CREATEDB REPLICATION"\
206
+ "#{" PASSWORD '#{@config[:password]}'" if @config[:password]}")
162
207
  end
163
208
  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))
209
+ raise(PostgresError.new("Failed to create database role #{@config[:username].inspect} by "\
210
+ "connecting to database #{tmp_config[:database].inspect} as role "\
211
+ "#{tmp_config[:username].inspect}. Please create the role manually.", e))
166
212
  end
167
213
  end
168
214
 
@@ -176,35 +222,45 @@ module Potluck
176
222
 
177
223
  begin
178
224
  Sequel.connect(tmp_config, logger: @logger) do |database|
179
- database.execute("CREATE DATABASE #{@config[:database]}")
225
+ database.execute("CREATE DATABASE \"#{@config[:database]}\"")
180
226
  end
181
227
  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))
228
+ raise(PostgresError.new("Failed to create database #{@config[:database].inspect} by connecting to "\
229
+ "database #{tmp_config[:database].inspect} as role #{tmp_config[:username].inspect}. "\
230
+ 'Please create the database manually.', e))
185
231
  end
186
232
  end
187
233
 
188
234
  ##
189
- # Content of the launchctl plist file.
235
+ # Grants appropriate permissions for the configured database role.
190
236
  #
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
- )
237
+ def grant_permissions
238
+ tmp_config = admin_database_config
239
+
240
+ begin
241
+ Sequel.connect(tmp_config, logger: @logger) do |db|
242
+ db.execute("GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"#{@config[:username]}\"")
243
+ db.execute("GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO \"#{@config[:username]}\"")
244
+ db.execute("ALTER DEFAULT PRIVILEGES FOR ROLE \"#{@config[:username]}\" IN SCHEMA public GRANT "\
245
+ "ALL PRIVILEGES ON TABLES TO \"#{@config[:username]}\"")
246
+ end
247
+ rescue => e
248
+ raise(PostgresError.new("Failed to grant database permissions for role "\
249
+ "#{@config[:username].inspect} by connecting as role #{tmp_config[:username].inspect}. Please "\
250
+ 'grant appropriate permissions manually.', e))
251
+ end
252
+ end
253
+
254
+ ##
255
+ # Returns a configuration hash for connecting to Postgres to perform administrative tasks (i.e. role and
256
+ # database creation). Uses the system user as the username and no password.
257
+ #
258
+ def admin_database_config
259
+ config = @config.dup
260
+ config[:username] = ENV['USER']
261
+ config[:password] = nil
262
+
263
+ config
208
264
  end
209
265
  end
210
266
  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.7
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: 2023-03-07 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.7
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.7
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -96,7 +96,9 @@ extra_rdoc_files: []
96
96
  files:
97
97
  - LICENSE
98
98
  - README.md
99
+ - VERSION
99
100
  - lib/potluck/postgres.rb
101
+ - lib/potluck/postgres/version.rb
100
102
  homepage: https://github.com/npickens/potluck/tree/master/potluck-postgres
101
103
  licenses:
102
104
  - MIT
@@ -119,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
119
121
  - !ruby/object:Gem::Version
120
122
  version: '0'
121
123
  requirements: []
122
- rubygems_version: 3.2.32
124
+ rubygems_version: 3.3.7
123
125
  signing_key:
124
126
  specification_version: 4
125
127
  summary: A Ruby manager for Postgres.