potluck-postgres 0.0.4 → 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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/potluck/postgres.rb +100 -36
  3. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2077db990cc6b953fe7034b0d0289a8442173a9734db26281cc37497a1887559
4
- data.tar.gz: 33ea6d44e01545a3c4002e8743243aac70f72c0b642e45584ecc7aabcda29fd4
3
+ metadata.gz: 667d6b357e4b021bba60a415b90f8623d9b72683ed3b5869ce87f2e43e033325
4
+ data.tar.gz: 656fd69e5541b414c9e67f750daabaf072c94fbf1237042424a3a5848cfedfa8
5
5
  SHA512:
6
- metadata.gz: 2a1ba7770728c55170bc6342ecc4b5f1a83e60524f0b434bcc2c7837dc43a7630ad30e24663fbda02ae73eef7e80ab40196b0e8585c95aeb60752cd660da84f1
7
- data.tar.gz: c325a86a0c061e29ccd4430ffc57dc284817722dc410bfb07ae0ab5bf8fea3b679eda1494eb4c784e4c0acea262cdc572140d83f14f6b3bc6c0033cd5848261b
6
+ metadata.gz: 03b852e0ce06ed33d0c7010779eb5bd6d1804e0151df4814a3ad218a50ffcaa27daebf47e7b7288ac7755c75ef4c11b003a82516f6a383e724dbbcdc4269759c
7
+ data.tar.gz: c3cfc79e9c846ca36e2da1015871cac23928bca1291bb7ee7c05ec8b459c71c4918bf057f70516da5dec9a6ab6942a4fc9d1428af970ab79bbfb20936039f5d9
@@ -4,6 +4,30 @@ require('potluck')
4
4
  require('sequel')
5
5
 
6
6
  module Potluck
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
+ #
7
31
  class Postgres < Service
8
32
  ROLE_NOT_FOUND_REGEX = /role .* does not exist/.freeze
9
33
  DATABASE_NOT_FOUND_REGEX = /database .* does not exist/.freeze
@@ -16,12 +40,29 @@ module Potluck
16
40
 
17
41
  attr_reader(:database)
18
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
+ #
19
49
  def initialize(config, **args)
20
50
  super(**args)
21
51
 
22
52
  @config = config
23
53
  end
24
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
+ #
25
66
  def connect
26
67
  (tries ||= 0) && (tries += 1)
27
68
  @database = Sequel.connect(@config, logger: @logger)
@@ -47,49 +88,27 @@ module Potluck
47
88
  sleep(1)
48
89
  retry
49
90
  elsif message.include?(CONNECTION_REFUSED_STRING)
50
- abort("#{e.class}: #{e.message.strip}")
91
+ raise(PostgresError.new(e.message.strip, e))
51
92
  else
52
- abort("#{e.class}: #{e.message.strip}\n #{e.backtrace.join("\n ")}")
93
+ raise
53
94
  end
54
95
  end
55
96
 
97
+ ##
98
+ # Disconnects from the database if a connection was made.
99
+ #
56
100
  def disconnect
57
101
  @database&.disconnect
58
102
  end
59
103
 
60
- def create_database_role
61
- tmp_config = @config.dup
62
- tmp_config[:database] = 'postgres'
63
- tmp_config[:username] = ENV['USER']
64
- tmp_config[:password] = nil
65
-
66
- begin
67
- Sequel.connect(tmp_config, logger: @logger) do |database|
68
- database.execute("CREATE ROLE #{@config[:username]} WITH LOGIN CREATEDB REPLICATION PASSWORD "\
69
- "'#{@config[:password]}'")
70
- end
71
- rescue => e
72
- @logger.error("#{e.class}: #{e.message.strip}\n #{e.backtrace.join("\n ")}\n")
73
- abort("Could not create role '#{@config[:username]}'. Make sure database user '#{ENV['USER']}' "\
74
- 'has permission to do so, or create it manually.')
75
- end
76
- end
77
-
78
- def create_database
79
- tmp_config = @config.dup
80
- tmp_config[:database] = 'postgres'
81
-
82
- begin
83
- Sequel.connect(tmp_config, logger: @logger) do |database|
84
- database.execute("CREATE DATABASE #{@config[:database]}")
85
- end
86
- rescue => e
87
- @logger.error("#{e.class}: #{e.message.strip}\n #{e.backtrace.join("\n ")}\n")
88
- abort("Could not create database '#{@config[:database]}'. Make sure database user "\
89
- "'#{@config[:username]}' has permission to do so, or create it manually.")
90
- end
91
- end
92
-
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
+ #
93
112
  def migrate(dir, steps = nil)
94
113
  return unless File.directory?(dir)
95
114
 
@@ -97,7 +116,7 @@ module Potluck
97
116
 
98
117
  # Suppress Sequel schema migration table queries.
99
118
  original_level = @logger.level
100
- @logger.level = Logger::WARN
119
+ @logger.level = Logger::WARN if @logger.level == Logger::INFO
101
120
 
102
121
  args = [Sequel::Model.db, dir, {allow_missing_migration_files: true}]
103
122
  migrator = Sequel::TimestampMigrator.new(*args)
@@ -120,10 +139,55 @@ module Potluck
120
139
  migrator = Sequel::TimestampMigrator.new(*args)
121
140
  @logger.level = original_level
122
141
  migrator.run
142
+ ensure
143
+ @logger.level = original_level if original_level
123
144
  end
124
145
 
125
146
  private
126
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
+ #
127
191
  def self.plist
128
192
  super(
129
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
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-12-28 00:00:00.000000000 Z
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,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.4
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.4
26
+ version: 0.0.5
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement