potluck-postgres 0.0.1 → 0.0.5
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 +4 -4
- data/lib/potluck/postgres.rb +120 -42
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 667d6b357e4b021bba60a415b90f8623d9b72683ed3b5869ce87f2e43e033325
|
4
|
+
data.tar.gz: 656fd69e5541b414c9e67f750daabaf072c94fbf1237042424a3a5848cfedfa8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03b852e0ce06ed33d0c7010779eb5bd6d1804e0151df4814a3ad218a50ffcaa27daebf47e7b7288ac7755c75ef4c11b003a82516f6a383e724dbbcdc4269759c
|
7
|
+
data.tar.gz: c3cfc79e9c846ca36e2da1015871cac23928bca1291bb7ee7c05ec8b459c71c4918bf057f70516da5dec9a6ab6942a4fc9d1428af970ab79bbfb20936039f5d9
|
data/lib/potluck/postgres.rb
CHANGED
@@ -1,17 +1,68 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require('potluck')
|
4
|
+
require('sequel')
|
4
5
|
|
5
6
|
module Potluck
|
6
|
-
|
7
|
+
##
|
8
|
+
# Error class used to wrap errors encountered while connecting to or setting up a database.
|
9
|
+
#
|
10
|
+
class PostgresError < ServiceError
|
11
|
+
attr_reader(:wrapped_error)
|
12
|
+
|
13
|
+
##
|
14
|
+
# Creates a new instance.
|
15
|
+
#
|
16
|
+
# * +message+ - Error message.
|
17
|
+
# * +wrapped_error+ - Original error that was rescued and is being wrapped by this one (optional).
|
18
|
+
#
|
19
|
+
def initialize(message, wrapped_error = nil)
|
20
|
+
super(message)
|
21
|
+
|
22
|
+
@wrapped_error = wrapped_error
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# A Ruby interface for controlling and connecting to Postgres. Uses
|
28
|
+
# [Sequel](https://github.com/jeremyevans/sequel) to connect and perform automatic role and database
|
29
|
+
# creation, as well as for utility methods such as database schema migration.
|
30
|
+
#
|
31
|
+
class Postgres < Service
|
32
|
+
ROLE_NOT_FOUND_REGEX = /role .* does not exist/.freeze
|
33
|
+
DATABASE_NOT_FOUND_REGEX = /database .* does not exist/.freeze
|
34
|
+
|
35
|
+
STARTING_UP_STRING = 'the database system is starting up'
|
36
|
+
STARTING_UP_TIMEOUT = 30
|
37
|
+
|
38
|
+
CONNECTION_REFUSED_STRING = 'connection refused'
|
39
|
+
CONNECTION_REFUSED_TIMEOUT = 3
|
40
|
+
|
7
41
|
attr_reader(:database)
|
8
42
|
|
43
|
+
##
|
44
|
+
# Creates a new instance.
|
45
|
+
#
|
46
|
+
# * +config+ - Configuration hash to pass to <tt>Sequel.connect</tt>.
|
47
|
+
# * +args+ - Arguments to pass to Potluck::Service.new (optional).
|
48
|
+
#
|
9
49
|
def initialize(config, **args)
|
10
50
|
super(**args)
|
11
51
|
|
12
52
|
@config = config
|
13
53
|
end
|
14
54
|
|
55
|
+
##
|
56
|
+
# Disconnects and stops the Postgres process.
|
57
|
+
#
|
58
|
+
def stop
|
59
|
+
disconnect
|
60
|
+
super
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Connects to the configured Postgres database.
|
65
|
+
#
|
15
66
|
def connect
|
16
67
|
(tries ||= 0) && (tries += 1)
|
17
68
|
@database = Sequel.connect(@config, logger: @logger)
|
@@ -21,61 +72,43 @@ module Potluck
|
|
21
72
|
Sequel.synchronize { Sequel::DATABASES.delete(dud) }
|
22
73
|
end
|
23
74
|
|
24
|
-
|
75
|
+
message = e.message.downcase
|
76
|
+
|
77
|
+
if message =~ ROLE_NOT_FOUND_REGEX && tries == 1
|
25
78
|
create_database_role
|
26
79
|
create_database
|
27
80
|
retry
|
28
|
-
elsif
|
81
|
+
elsif message =~ DATABASE_NOT_FOUND_REGEX && tries == 1
|
29
82
|
create_database
|
30
83
|
retry
|
31
|
-
elsif (
|
32
|
-
e.message.include?('the database system is starting up'))
|
84
|
+
elsif message.include?(STARTING_UP_STRING) && tries < STARTING_UP_TIMEOUT
|
33
85
|
sleep(1)
|
34
86
|
retry
|
35
|
-
elsif
|
36
|
-
|
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))
|
37
92
|
else
|
38
|
-
|
93
|
+
raise
|
39
94
|
end
|
40
95
|
end
|
41
96
|
|
97
|
+
##
|
98
|
+
# Disconnects from the database if a connection was made.
|
99
|
+
#
|
42
100
|
def disconnect
|
43
101
|
@database&.disconnect
|
44
102
|
end
|
45
103
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
database.execute("CREATE ROLE #{@config[:username]} WITH LOGIN CREATEDB REPLICATION PASSWORD "\
|
55
|
-
"'#{@config[:password]}'")
|
56
|
-
end
|
57
|
-
rescue => e
|
58
|
-
@logger.error("#{e.class}: #{e.message.strip}\n #{e.backtrace.join("\n ")}\n")
|
59
|
-
abort("Could not create role '#{@config[:username]}'. Make sure database user '#{ENV['USER']}' "\
|
60
|
-
'has permission to do so, or create it manually.')
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
def create_database
|
65
|
-
tmp_config = @config.dup
|
66
|
-
tmp_config[:database] = 'postgres'
|
67
|
-
|
68
|
-
begin
|
69
|
-
Sequel.connect(tmp_config, logger: @logger) do |database|
|
70
|
-
database.execute("CREATE DATABASE #{@config[:database]}")
|
71
|
-
end
|
72
|
-
rescue => e
|
73
|
-
@logger.error("#{e.class}: #{e.message.strip}\n #{e.backtrace.join("\n ")}\n")
|
74
|
-
abort("Could not create database '#{@config[:database]}'. Make sure database user "\
|
75
|
-
"'#{@config[:username]}' has permission to do so, or create it manually.")
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
104
|
+
##
|
105
|
+
# Runs database migrations by way of Sequel's migration extension. Migration files must use the
|
106
|
+
# timestamp naming strategy as opposed to integers.
|
107
|
+
#
|
108
|
+
# * +dir+ - Directory where migration files are located.
|
109
|
+
# * +steps+ - Number of steps forward or backward to migrate from the current migration, otherwise will
|
110
|
+
# migrate to latest (optional).
|
111
|
+
#
|
79
112
|
def migrate(dir, steps = nil)
|
80
113
|
return unless File.directory?(dir)
|
81
114
|
|
@@ -83,7 +116,7 @@ module Potluck
|
|
83
116
|
|
84
117
|
# Suppress Sequel schema migration table queries.
|
85
118
|
original_level = @logger.level
|
86
|
-
@logger.level = Logger::WARN
|
119
|
+
@logger.level = Logger::WARN if @logger.level == Logger::INFO
|
87
120
|
|
88
121
|
args = [Sequel::Model.db, dir, {allow_missing_migration_files: true}]
|
89
122
|
migrator = Sequel::TimestampMigrator.new(*args)
|
@@ -106,10 +139,55 @@ module Potluck
|
|
106
139
|
migrator = Sequel::TimestampMigrator.new(*args)
|
107
140
|
@logger.level = original_level
|
108
141
|
migrator.run
|
142
|
+
ensure
|
143
|
+
@logger.level = original_level if original_level
|
109
144
|
end
|
110
145
|
|
111
146
|
private
|
112
147
|
|
148
|
+
##
|
149
|
+
# Attempts to connect to the 'postgres' database as the system user with no password and create the
|
150
|
+
# configured role. Useful in development.
|
151
|
+
#
|
152
|
+
def create_database_role
|
153
|
+
tmp_config = @config.dup
|
154
|
+
tmp_config[:database] = 'postgres'
|
155
|
+
tmp_config[:username] = ENV['USER']
|
156
|
+
tmp_config[:password] = nil
|
157
|
+
|
158
|
+
begin
|
159
|
+
Sequel.connect(tmp_config, logger: @logger) do |database|
|
160
|
+
database.execute("CREATE ROLE #{@config[:username]} WITH LOGIN CREATEDB REPLICATION PASSWORD "\
|
161
|
+
"'#{@config[:password]}'")
|
162
|
+
end
|
163
|
+
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))
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
##
|
170
|
+
# Attempts to connect to the 'postgres' database with the configured user and password and create the
|
171
|
+
# configured database. Useful in development.
|
172
|
+
#
|
173
|
+
def create_database
|
174
|
+
tmp_config = @config.dup
|
175
|
+
tmp_config[:database] = 'postgres'
|
176
|
+
|
177
|
+
begin
|
178
|
+
Sequel.connect(tmp_config, logger: @logger) do |database|
|
179
|
+
database.execute("CREATE DATABASE #{@config[:database]}")
|
180
|
+
end
|
181
|
+
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))
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
##
|
189
|
+
# Content of the launchctl plist file.
|
190
|
+
#
|
113
191
|
def self.plist
|
114
192
|
super(
|
115
193
|
<<~EOS
|
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.
|
4
|
+
version: 0.0.5
|
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-
|
11
|
+
date: 2021-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: potluck
|
@@ -16,14 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.0.
|
19
|
+
version: 0.0.5
|
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.
|
26
|
+
version: 0.0.5
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: pg
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: sequel
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -105,7 +119,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
119
|
- !ruby/object:Gem::Version
|
106
120
|
version: '0'
|
107
121
|
requirements: []
|
108
|
-
rubygems_version: 3.2.
|
122
|
+
rubygems_version: 3.2.32
|
109
123
|
signing_key:
|
110
124
|
specification_version: 4
|
111
125
|
summary: A Ruby manager for Postgres.
|