postgresinator 0.1.0 → 0.2.0
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.
- data/lib/postgresinator/built-in.rb +198 -0
- data/lib/postgresinator/check.rb +130 -0
- data/lib/postgresinator/config.rb +69 -118
- data/lib/postgresinator/db.rb +126 -0
- data/lib/postgresinator/examples/Capfile +1 -1
- data/lib/postgresinator/examples/Dockerfile +7 -6
- data/lib/postgresinator/examples/config/deploy/staging.rb +29 -50
- data/lib/postgresinator/examples/config/deploy.rb +8 -58
- data/lib/postgresinator/examples/pg_hba.conf.erb +5 -7
- data/lib/postgresinator/examples/postgresql.conf.erb +6 -6
- data/lib/postgresinator/examples/recovery.conf.erb +2 -2
- data/lib/postgresinator/pg.rb +117 -486
- data/lib/postgresinator.rb +4 -1
- metadata +25 -24
- data/lib/postgresinator/examples/config/deploy/staging_postgresinator.rb +0 -43
- data/lib/postgresinator/examples/config/deploy_postgresinator.rb +0 -1
data/lib/postgresinator/pg.rb
CHANGED
@@ -1,528 +1,159 @@
|
|
1
|
-
require 'erb'
|
2
|
-
|
3
|
-
## NOTES:
|
4
|
-
# tasks without 'desc' description lines are for manual debugging of this
|
5
|
-
# deployment code.
|
6
|
-
#
|
7
|
-
# only pass strings (if anything) to tasks. this allows tasks to be
|
8
|
-
# debugged individually. only private methods take ruby objects.
|
9
|
-
|
10
1
|
namespace :pg do
|
11
2
|
|
12
|
-
task :ensure_setup => ['
|
13
|
-
|
14
|
-
|
15
|
-
Rake::Task['
|
16
|
-
Rake::Task['config:database_not_found'].invoke(args.database_name) unless args.database_name.nil?
|
17
|
-
Rake::Task['config:domain_not_found'].invoke(args.domain) unless args.domain.nil?
|
18
|
-
Rake::Task['config:role_not_found'].invoke(args.role_name) unless(args.role_name.nil? or args.force == "true")
|
3
|
+
task :ensure_setup => ['pg:check:settings', 'deployinator:sshkit_umask'] do |t, args|
|
4
|
+
SSHKit.config.output_verbosity = fetch(:postgres_log_level)
|
5
|
+
Rake::Task['pg:check:settings:database'].invoke(args.database_name) unless args.database_name.nil?
|
6
|
+
Rake::Task['pg:check:settings:domain'].invoke(args.domain) unless args.domain.nil?
|
19
7
|
end
|
20
8
|
|
21
|
-
desc "Idempotently setup one or more PostgreSQL instances
|
22
|
-
task :setup => :ensure_setup do
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
config_file_changed = false
|
34
|
-
cluster.image.config_files.each do |config_file|
|
35
|
-
next if config_file == "recovery.conf"
|
36
|
-
if pg_config_file_differs?(cluster, server, config_file)
|
37
|
-
warn "Config file #{config_file} on #{server.domain} is being updated."
|
38
|
-
Rake::Task['pg:install_config_file'].invoke(server.domain, config_file)
|
39
|
-
Rake::Task['pg:install_config_file'].reenable
|
40
|
-
config_file_changed = true
|
41
|
-
end
|
42
|
-
end
|
43
|
-
unless pg_container_exists?(server)
|
44
|
-
# the create_container task's prerequisite task :ensure_config_files is
|
45
|
-
# for manual use of create_container, so here we clear_prerequisites.
|
46
|
-
Rake::Task['pg:create_container'].clear_prerequisites
|
47
|
-
Rake::Task['pg:create_container'].invoke(server.domain)
|
48
|
-
Rake::Task['pg:create_container'].reenable
|
49
|
-
else
|
50
|
-
unless pg_container_is_running?(server)
|
51
|
-
Rake::Task['pg:start_container'].invoke(server.domain)
|
52
|
-
Rake::Task['pg:start_container'].reenable
|
53
|
-
else
|
54
|
-
if config_file_changed
|
55
|
-
Rake::Task['pg:restart_container'].invoke(server.domain)
|
56
|
-
Rake::Task['pg:restart_container'].reenable
|
57
|
-
else
|
58
|
-
info "No config file changes for #{server.container_name} and it is already running; we're setup!"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
# sleep to allow postgres to start up before running subsequent commands against it
|
63
|
-
sleep 3
|
64
|
-
if server.master
|
65
|
-
unless pg_role_exists?(cluster, server, "replicator")
|
66
|
-
info "Creating role 'replicator' #{server.domain}"
|
67
|
-
Rake::Task['pg:create_role'].invoke(server.domain, "replicator")
|
68
|
-
Rake::Task['pg:create_role'].reenable
|
69
|
-
end
|
70
|
-
cluster.databases.each do |database|
|
71
|
-
unless pg_role_exists?(cluster, server, database.db_role)
|
72
|
-
info "Creating role #{database.db_role} on #{server.domain}"
|
73
|
-
Rake::Task['pg:create_role'].invoke(server.domain, database.db_role)
|
74
|
-
Rake::Task['pg:create_role'].reenable
|
75
|
-
end
|
76
|
-
unless pg_database_exists?(cluster, server, database)
|
77
|
-
info "Creating database #{database.name} on #{server.domain}"
|
78
|
-
Rake::Task['pg:create_database'].invoke(server.domain, database.name)
|
79
|
-
Rake::Task['pg:create_database'].reenable
|
80
|
-
Rake::Task['pg:grant_database'].invoke(server.domain, database.name)
|
81
|
-
Rake::Task['pg:grant_database'].reenable
|
82
|
-
end
|
83
|
-
end
|
9
|
+
desc "Idempotently setup one or more PostgreSQL instances and their databases."
|
10
|
+
task :setup => [:ensure_setup, 'postgresinator:deployment_user', 'pg:check:firewall', 'pg:check:settings:postgres_uid_gid'] do
|
11
|
+
Rake::Task['pg:install_config_files'].invoke
|
12
|
+
on roles(:db) do |host|
|
13
|
+
name = host.properties.postgres_container_name
|
14
|
+
if container_exists?(name)
|
15
|
+
existing_container_start_or_restart_if_needed(host)
|
16
|
+
else
|
17
|
+
fatal_message = "#{fetch(:postgres_data_path)} on #{host} is not empty, cannot continue! " +
|
18
|
+
"You'll need to delete those files by hand. Be sure you are not deleting important data!"
|
19
|
+
as :root do
|
20
|
+
fatal fatal_message and exit if files_in_directory?(fetch(:postgres_data_path))
|
84
21
|
end
|
22
|
+
pg_init(host)
|
23
|
+
install_ssl_key_crt(host)
|
24
|
+
warn "Starting a new container named #{name} on #{host}"
|
25
|
+
pg_run(host)
|
26
|
+
check_stayed_running(name)
|
85
27
|
end
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
Rake::Task['pg:status'].reenable
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
desc "Check the status of the PostgreSQL instance on 'domain'."
|
99
|
-
task :status, [:domain] => :ensure_setup do |t, args|
|
100
|
-
cluster = @cluster
|
101
|
-
server = cluster.servers.select { |s| s.domain == args.domain }.first
|
102
|
-
on "#{cluster.ssh_user}@#{server.domain}" do
|
103
|
-
if pg_container_exists?(server)
|
104
|
-
info "#{server.container_name} exists on #{server.domain}"
|
105
|
-
if pg_container_is_running?(server)
|
106
|
-
info ""
|
107
|
-
info "#{server.container_name} is running on #{server.domain}"
|
108
|
-
info ""
|
109
|
-
Rake::Task['pg:list_roles'].invoke(server.domain)
|
110
|
-
Rake::Task['pg:list_roles'].reenable
|
111
|
-
info ""
|
112
|
-
Rake::Task['pg:list_databases'].invoke(server.domain)
|
113
|
-
Rake::Task['pg:list_databases'].reenable
|
114
|
-
info ""
|
115
|
-
Rake::Task['pg:streaming_status'].invoke(server.domain)
|
116
|
-
Rake::Task['pg:streaming_status'].reenable
|
117
|
-
else
|
118
|
-
info "#{server.container_name} is not running on #{server.domain}"
|
119
|
-
end
|
120
|
-
else
|
121
|
-
info "#{server.container_name} does not exist on #{server.domain}"
|
28
|
+
set :master_container_running, false
|
29
|
+
set :master_container_running, container_is_running?(name)
|
30
|
+
set :master_container_port, host.properties.postgres_port
|
31
|
+
# sleep to allow postgres to start up before running subsequent commands against it
|
32
|
+
sleep 3
|
33
|
+
unless pg_role_exists?("replicator")
|
34
|
+
info "Creating postgres role 'replicator'."
|
35
|
+
pg_create_role("replicator", fetch(:postgres_replicator_pass))
|
122
36
|
end
|
123
37
|
end
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
on "#{cluster.ssh_user}@#{server.domain}" do
|
133
|
-
if server.master
|
134
|
-
clean = ""
|
135
|
-
unless pg_database_empty?(cluster, server, database)
|
136
|
-
if pg_confirm_database_overwrite?(server, database); clean = "--clean"; else exit(0); end
|
38
|
+
on roles(:db_slave, :in => :parallel) do |host|
|
39
|
+
name = host.properties.postgres_container_name
|
40
|
+
unless container_exists?(name)
|
41
|
+
fatal "Master must be running before creating a slave" and exit unless fetch(:master_container_running)
|
42
|
+
fatal_message = "#{fetch(:postgres_data_path)} on #{host} is not empty, cannot continue! " +
|
43
|
+
"You'll need to delete those files by hand. Be sure you are not deleting important data!"
|
44
|
+
as :root do
|
45
|
+
fatal fatal_message and exit if files_in_directory?(fetch(:postgres_data_path))
|
137
46
|
end
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
desc "Dump 'database_name' into /tmp/'dump_file' on the master server."
|
151
|
-
task :dump, [:dump_file, :database_name] => :ensure_setup do |t, args|
|
152
|
-
cluster = @cluster
|
153
|
-
database = cluster.databases.select { |d| d.name == args.database_name }.first
|
154
|
-
# we only dump from the master server
|
155
|
-
server = cluster.servers.select { |s| s.master }.first
|
156
|
-
on "#{cluster.ssh_user}@#{server.domain}" do
|
157
|
-
if server.master
|
158
|
-
pg_confirm_file_overwrite?(server, args.dump_file) if pg_file_exists?("/tmp/#{args.dump_file}")
|
159
|
-
execute("docker", "run", "--rm",
|
160
|
-
"--volume", "/tmp:/tmp:rw",
|
161
|
-
"--entrypoint", "/bin/bash",
|
162
|
-
"--volumes-from", server.container_name,
|
163
|
-
cluster.image.name,
|
164
|
-
"-c", "'/usr/bin/pg_dump", "-U", "postgres", "-F", "tar",
|
165
|
-
"-v", database.name, ">", "/tmp/#{args.dump_file}'"
|
166
|
-
)
|
47
|
+
pg_replicate(host)
|
48
|
+
install_ssl_key_crt(host)
|
49
|
+
install_recovery_conf
|
50
|
+
warn "Starting a new container named #{name} on #{host}"
|
51
|
+
pg_run(host)
|
52
|
+
check_stayed_running(name)
|
53
|
+
else
|
54
|
+
existing_container_start_or_restart_if_needed(host)
|
167
55
|
end
|
168
56
|
end
|
57
|
+
Rake::Task['pg:db:setup'].invoke
|
169
58
|
end
|
170
59
|
|
171
|
-
desc "
|
172
|
-
task :
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
task :ensure_config_files, [:domain] => :ensure_setup do |t, args|
|
182
|
-
cluster = @cluster
|
183
|
-
server = cluster.servers.select { |s| s.domain == args.domain }.first
|
184
|
-
on "#{cluster.ssh_user}@#{server.domain}" do
|
185
|
-
cluster.image.config_files.each do |config_file|
|
186
|
-
next if config_file == "recovery.conf"
|
187
|
-
if pg_config_file_differs?(cluster, server, config_file)
|
188
|
-
Rake::Task['pg:install_config_file'].invoke(server.domain, config_file)
|
189
|
-
Rake::Task['pg:install_config_file'].reenable
|
60
|
+
desc "Check the statuses of each PostgreSQL instance."
|
61
|
+
task :status => [:ensure_setup, 'postgresinator:deployment_user'] do
|
62
|
+
on roles(:db, :db_slave, :in => :sequence) do |host|
|
63
|
+
name = host.properties.postgres_container_name
|
64
|
+
if container_exists?(name)
|
65
|
+
if container_is_running?(name)
|
66
|
+
info "#{name} exists and is running on #{host}"
|
67
|
+
else
|
68
|
+
info "#{name} exists on #{host} but is not running."
|
190
69
|
end
|
70
|
+
else
|
71
|
+
info "#{name} does not exist on #{host}"
|
191
72
|
end
|
192
73
|
end
|
193
74
|
end
|
194
75
|
|
195
|
-
task :
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
unless server.master
|
200
|
-
on "#{cluster.ssh_user}@#{master_server.domain}" do
|
201
|
-
fatal "Master must be running before creating a slave" and raise unless pg_container_is_running?(master_server)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
on "#{cluster.ssh_user}@#{server.domain}" do
|
205
|
-
warn "Starting a new container named #{server.container_name} on #{server.domain}"
|
76
|
+
task :install_config_files => [:ensure_setup, 'postgresinator:deployment_user'] do
|
77
|
+
require 'erb' unless defined?(ERB)
|
78
|
+
on roles(:db, :db_slave, :in => :parallel) do |host|
|
79
|
+
host.properties.set :config_file_changed, false
|
206
80
|
as 'root' do
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
Rake::Task['pg:link_keys'].reenable
|
219
|
-
Rake::Task['pg:install_config_file'].invoke(server.domain, "recovery.conf")
|
220
|
-
Rake::Task['pg:install_config_file'].reenable
|
81
|
+
execute "mkdir", "-p", fetch(:postgres_config_path)
|
82
|
+
fetch(:postgres_config_files).each do |config_file|
|
83
|
+
template_path = File.expand_path("#{fetch(:postgres_templates_path)}/#{config_file}.erb")
|
84
|
+
generated_config_file = ERB.new(File.new(template_path).read).result(binding)
|
85
|
+
upload! StringIO.new(generated_config_file), "/tmp/#{config_file}.file"
|
86
|
+
unless test "diff", "-q", "/tmp/#{config_file}.file", "#{fetch(:postgres_config_path)}/#{config_file}"
|
87
|
+
warn "Config file #{config_file} on #{host} is being updated."
|
88
|
+
execute("cp", "/tmp/#{config_file}.file", "#{fetch(:postgres_config_path)}/#{config_file}")
|
89
|
+
host.properties.set :config_file_changed, true
|
90
|
+
end
|
91
|
+
execute "rm", "/tmp/#{config_file}.file"
|
221
92
|
end
|
222
|
-
execute("docker", "run", server.docker_run_command)
|
223
93
|
end
|
224
94
|
end
|
225
95
|
end
|
226
96
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
execute("docker", "restart", server.container_name)
|
233
|
-
sleep 2
|
234
|
-
fatal pg_stay_running_message(server) and raise unless pg_container_is_running?(server)
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
task :start_container, [:domain] => :ensure_setup do |t, args|
|
239
|
-
cluster = @cluster
|
240
|
-
server = cluster.servers.select { |s| s.domain == args.domain }.first
|
241
|
-
on "#{cluster.ssh_user}@#{server.domain}" do
|
242
|
-
warn "Starting an existing but non-running container named #{server.container_name}"
|
243
|
-
execute("docker", "start", server.container_name)
|
244
|
-
sleep 2
|
245
|
-
fatal pg_stay_running_message(server) and raise unless pg_container_is_running?(server)
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
task :ensure_access_docker, [:domain] => :ensure_setup do |t, args|
|
250
|
-
cluster = @cluster
|
251
|
-
on "#{cluster.ssh_user}@#{args.domain}" do
|
252
|
-
as cluster.ssh_user do
|
253
|
-
unless test("bash", "-c", "\"docker", "ps", "&>", "/dev/null\"")
|
254
|
-
execute("sudo", "usermod", "-a", "-G", "docker", cluster.ssh_user)
|
255
|
-
fatal "Newly added to docker group, this run will fail, next run will succeed. Simply try again."
|
97
|
+
def install_ssl_key_crt(host)
|
98
|
+
as 'root' do
|
99
|
+
[fetch(:postgres_ssl_key), fetch(:postgres_ssl_crt)].each do |file|
|
100
|
+
if test("[", "-L", file, "]") or file_exists?(file)
|
101
|
+
execute("rm", file)
|
256
102
|
end
|
257
103
|
end
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
# TODO: get this recovery.conf dependancy out of here?
|
267
|
-
path = args.config_file == "recovery.conf" ? server.data_path : server.conf_path
|
268
|
-
execute("mkdir", "-p", path) unless test("test", "-d", path)
|
269
|
-
generated_config_file = pg_generate_config_file(cluster, server, args.config_file)
|
270
|
-
upload! StringIO.new(generated_config_file), "/tmp/#{args.config_file}"
|
271
|
-
execute("mv", "/tmp/#{args.config_file}", "#{path}/#{args.config_file}")
|
272
|
-
execute("chown", "-R", "#{cluster.image.postgres_uid}:#{cluster.image.postgres_gid}", path)
|
273
|
-
execute("chmod", "700", path)
|
104
|
+
pg_ssl_key(host)
|
105
|
+
pg_ssl_crt(host)
|
106
|
+
execute("rm", fetch(:postgres_ssl_csr))
|
107
|
+
execute("chmod", "0600", fetch(:postgres_ssl_key))
|
108
|
+
execute("chmod", "0600", fetch(:postgres_ssl_crt))
|
109
|
+
[fetch(:postgres_ssl_key), fetch(:postgres_ssl_crt)].each do |file|
|
110
|
+
execute("chown", "#{fetch(:postgres_uid)}:#{fetch(:postgres_gid)}", file)
|
111
|
+
execute("chmod", "0600", file)
|
274
112
|
end
|
275
113
|
end
|
276
114
|
end
|
277
115
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
"-c", "\"\\du\"'").each_line { |line| info line }
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
task :list_databases, [:domain] => :ensure_setup do |t, args|
|
292
|
-
cluster = @cluster
|
293
|
-
server = cluster.servers.select { |s| s.domain == args.domain }.first
|
294
|
-
on "#{cluster.ssh_user}@#{args.domain}" do
|
295
|
-
capture("docker", "run", "--rm",
|
296
|
-
"--entrypoint", "/bin/bash",
|
297
|
-
"--volumes-from", server.container_name,
|
298
|
-
cluster.image.name,
|
299
|
-
"-c", "'/usr/bin/psql", "-U", "postgres",
|
300
|
-
"-a", "-c", "\"\\l\"'").each_line { |line| info line }
|
116
|
+
def install_recovery_conf
|
117
|
+
as 'root' do
|
118
|
+
path = "#{fetch(:postgres_data_path)}/recovery.conf"
|
119
|
+
template_path = File.expand_path("#{fetch(:postgres_templates_path)}/recovery.conf.erb")
|
120
|
+
generated_config_file = ERB.new(File.new(template_path).read).result(binding)
|
121
|
+
upload! StringIO.new(generated_config_file), "/tmp/recovery_conf"
|
122
|
+
execute("mv", "/tmp/recovery_conf", path)
|
123
|
+
execute("chown", "#{fetch(:postgres_uid)}:#{fetch(:postgres_gid)}", path)
|
124
|
+
execute("chmod", "0640", path)
|
301
125
|
end
|
302
126
|
end
|
303
127
|
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
"docker", "run", "--rm", "--interactive",
|
312
|
-
"--entrypoint", "/bin/bash",
|
313
|
-
"--volumes-from", server.container_name,
|
314
|
-
cluster.image.name,
|
315
|
-
"-c", "'/usr/bin/psql", "-U", "postgres", "-xa'").each_line { |line| info line }
|
128
|
+
def existing_container_start_or_restart_if_needed(host)
|
129
|
+
name = host.properties.postgres_container_name
|
130
|
+
if container_is_running?(name)
|
131
|
+
if container_is_restarting?(name)
|
132
|
+
restart_container(name)
|
133
|
+
elsif host.properties.config_file_changed
|
134
|
+
ask_reload_or_restart(name, host)
|
316
135
|
else
|
317
|
-
|
318
|
-
|
319
|
-
capture("echo", "\"SELECT", "now()", "-", "pg_last_xact_replay_timestamp()",
|
320
|
-
"AS", "replication_delay;\"", "|",
|
321
|
-
"docker", "run", "--rm", "--interactive",
|
322
|
-
"--entrypoint", "/bin/bash",
|
323
|
-
"--volumes-from", server.container_name,
|
324
|
-
cluster.image.name,
|
325
|
-
"-c", "'/usr/bin/psql", "-U", "postgres'").each_line { |line| info line }
|
136
|
+
info("No config file changes for #{name} " +
|
137
|
+
"on #{host} and it is already running; we're setup!")
|
326
138
|
end
|
139
|
+
else
|
140
|
+
start_container(name)
|
327
141
|
end
|
328
142
|
end
|
329
143
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
"-c", "'/usr/bin/psql", "-U", "postgres'")
|
144
|
+
def ask_reload_or_restart(name, host)
|
145
|
+
warn "A config file has changed for #{name} on #{host}, please specify " +
|
146
|
+
"whether you would like to have PostgreSQL reload the config, or restart itself"
|
147
|
+
ask :reload_or_restart, nil
|
148
|
+
case fetch(:reload_or_restart).chomp.downcase
|
149
|
+
when "reload"
|
150
|
+
execute("docker", "kill", "-s", "SIGHUP", name)
|
151
|
+
when "restart"
|
152
|
+
restart_container(name)
|
153
|
+
else
|
154
|
+
warn "Please enter 'reload' or 'restart'"
|
155
|
+
ask_reload_or_restart(name, host)
|
343
156
|
end
|
344
157
|
end
|
345
158
|
|
346
|
-
task :create_database, [:domain, :database_name] => :ensure_setup do |t, args|
|
347
|
-
cluster = @cluster
|
348
|
-
database = cluster.databases.select { |d| d.name == args.database_name }.first
|
349
|
-
server = cluster.servers.select { |s| s.domain == args.domain }.first
|
350
|
-
on "#{cluster.ssh_user}@#{args.domain}" do
|
351
|
-
execute("echo", "\"CREATE", "DATABASE", "\\\"#{database.name}\\\"",
|
352
|
-
"WITH", "OWNER", "\\\"#{database.db_role}\\\"", "TEMPLATE",
|
353
|
-
"template0", "ENCODING", "'UTF8';\"", "|",
|
354
|
-
"docker", "run", "--rm", "--interactive",
|
355
|
-
"--entrypoint", "/bin/bash",
|
356
|
-
"--volumes-from", server.container_name,
|
357
|
-
cluster.image.name,
|
358
|
-
"-c", "'/usr/bin/psql", "-U", "postgres'")
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
task :grant_database, [:domain, :database_name] => :ensure_setup do |t, args|
|
363
|
-
cluster = @cluster
|
364
|
-
database = cluster.databases.select { |d| d.name == args.database_name }.first
|
365
|
-
server = cluster.servers.select { |s| s.domain == args.domain }.first
|
366
|
-
on "#{cluster.ssh_user}@#{args.domain}" do
|
367
|
-
execute("echo", "\"GRANT", "ALL", "PRIVILEGES", "ON", "DATABASE",
|
368
|
-
"\\\"#{database.name}\\\"", "to", "\\\"#{database.db_role}\\\";\"", "|",
|
369
|
-
"docker", "run", "--rm", "--interactive",
|
370
|
-
"--entrypoint", "/bin/bash",
|
371
|
-
"--volumes-from", server.container_name,
|
372
|
-
cluster.image.name,
|
373
|
-
"-c", "'/usr/bin/psql", "-U", "postgres'")
|
374
|
-
end
|
375
|
-
end
|
376
|
-
|
377
|
-
task :link_keys, [:domain] => :ensure_setup do |t, args|
|
378
|
-
cluster = @cluster
|
379
|
-
server = cluster.servers.select { |s| s.domain == args.domain }.first
|
380
|
-
on "#{cluster.ssh_user}@#{args.domain}" do
|
381
|
-
as "root" do
|
382
|
-
inner_server_crt = "#{cluster.image.data_path}/server.crt"
|
383
|
-
outer_server_crt = "#{server.data_path}/server.crt"
|
384
|
-
unless pg_file_exists?(outer_server_crt)
|
385
|
-
execute("docker", "run",
|
386
|
-
"--rm", "--user", "root", "--entrypoint", "/bin/ln",
|
387
|
-
"--volume", "#{server.data_path}:#{cluster.image.data_path}:rw",
|
388
|
-
cluster.image.name, "-s", "/etc/ssl/certs/ssl-cert-snakeoil.pem", inner_server_crt
|
389
|
-
)
|
390
|
-
execute("docker", "run",
|
391
|
-
"--rm", "--user", "root", "--entrypoint", "/bin/chown",
|
392
|
-
"--volume", "#{server.data_path}:#{cluster.image.data_path}:rw",
|
393
|
-
cluster.image.name, "root:root", inner_server_crt
|
394
|
-
)
|
395
|
-
end
|
396
|
-
inner_server_key = "#{cluster.image.data_path}/server.key"
|
397
|
-
outer_server_key = "#{server.data_path}/server.key"
|
398
|
-
unless pg_file_exists?(outer_server_key)
|
399
|
-
execute("docker", "run",
|
400
|
-
"--rm", "--user", "root", "--entrypoint", "/bin/ln",
|
401
|
-
"--volume", "#{server.data_path}:#{cluster.image.data_path}:rw",
|
402
|
-
cluster.image.name, "-s", "/etc/ssl/private/ssl-cert-snakeoil.key", inner_server_key
|
403
|
-
)
|
404
|
-
execute("docker", "run",
|
405
|
-
"--rm", "--user", "root", "--entrypoint", "/bin/chown",
|
406
|
-
"--volume", "#{server.data_path}:#{cluster.image.data_path}:rw",
|
407
|
-
cluster.image.name, "root:root", inner_server_key
|
408
|
-
)
|
409
|
-
end
|
410
|
-
end
|
411
|
-
end
|
412
|
-
end
|
413
|
-
|
414
|
-
task :open_firewall, [:domain] => :ensure_setup do |t, args|
|
415
|
-
cluster = @cluster
|
416
|
-
server = cluster.servers.select { |s| s.domain == args.domain }.first
|
417
|
-
on "#{cluster.ssh_user}@#{args.domain}" do
|
418
|
-
as "root" do
|
419
|
-
if test "ufw", "status"
|
420
|
-
raise "Error during opening UFW firewall" unless test("ufw", "allow", "#{server.port}/tcp")
|
421
|
-
end
|
422
|
-
end
|
423
|
-
end
|
424
|
-
end
|
425
|
-
|
426
|
-
private
|
427
|
-
|
428
|
-
# Temporarily added 'pg_' to the beginning of each of these methods to avoid
|
429
|
-
# getting them overwritten by other gems with methods with the same names, (E.G. nginxinator.)
|
430
|
-
## TODO Figure out how to do this the right or better way.
|
431
|
-
def pg_stay_running_message(server)
|
432
|
-
"Container #{server.container_name} on #{server.domain} did not stay running more than 2 seconds"
|
433
|
-
end
|
434
|
-
|
435
|
-
def pg_files_in_data_path?(server)
|
436
|
-
test("[", "\"$(ls", "-A", "#{server.data_path})\"", "]")
|
437
|
-
end
|
438
|
-
|
439
|
-
def pg_config_file_differs?(cluster, server, config_file)
|
440
|
-
generated_config_file = pg_generate_config_file(cluster, server, config_file)
|
441
|
-
as 'root' do
|
442
|
-
if pg_file_exists?("#{server.conf_path}/#{config_file}")
|
443
|
-
capture("cat", "#{server.conf_path}/#{config_file}").chomp != generated_config_file.chomp
|
444
|
-
else
|
445
|
-
true
|
446
|
-
end
|
447
|
-
end
|
448
|
-
end
|
449
|
-
|
450
|
-
def pg_generate_config_file(cluster, server, config_file)
|
451
|
-
@cluster = cluster # needed for ERB
|
452
|
-
@server = server # needed for ERB
|
453
|
-
template_path = File.expand_path("templates/postgresql/#{config_file}.erb")
|
454
|
-
ERB.new(File.new(template_path).read).result(binding)
|
455
|
-
end
|
456
|
-
|
457
|
-
def pg_role_exists?(cluster, server, db_role)
|
458
|
-
test("echo", "\"SELECT", "*", "FROM", "pg_user", "WHERE", "usename", "=", "'#{db_role}';\"", "|",
|
459
|
-
"docker", "run", "--rm", "--interactive",
|
460
|
-
"--entrypoint", "/bin/bash",
|
461
|
-
"--volumes-from", server.container_name,
|
462
|
-
cluster.image.name,
|
463
|
-
"-c", "'/usr/bin/psql", "-U", "postgres'", "|",
|
464
|
-
"grep", "-q", "'#{db_role}'")
|
465
|
-
end
|
466
|
-
|
467
|
-
def pg_database_exists?(cluster, server, database)
|
468
|
-
test "docker", "run", "--rm",
|
469
|
-
"--entrypoint", "/bin/bash",
|
470
|
-
"--volumes-from", server.container_name,
|
471
|
-
cluster.image.name,
|
472
|
-
"-c", "'/usr/bin/psql", "-U", "postgres", "-lqt", "|",
|
473
|
-
"cut", "-d\\|", "-f1", "|", "grep", "-w", "#{database.name}'"
|
474
|
-
end
|
475
|
-
|
476
|
-
def pg_container_exists?(server)
|
477
|
-
test "docker", "inspect", server.container_name, ">", "/dev/null"
|
478
|
-
end
|
479
|
-
|
480
|
-
def pg_container_is_running?(server)
|
481
|
-
(capture "docker", "inspect",
|
482
|
-
"--format='{{.State.Running}}'",
|
483
|
-
server.container_name).strip == "true"
|
484
|
-
end
|
485
|
-
|
486
|
-
def pg_file_exists?(file_name_path)
|
487
|
-
test "[", "-f", file_name_path, "]"
|
488
|
-
end
|
489
|
-
|
490
|
-
def pg_confirm_file_overwrite?(server, dump_file)
|
491
|
-
warn "A file named #{dump_file} already exists on #{server.domain} in /tmp. If you continue, you will overwrite it."
|
492
|
-
warn "Are you positive(Y/N)?"
|
493
|
-
STDOUT.flush
|
494
|
-
case STDIN.gets.chomp.upcase
|
495
|
-
when "Y"
|
496
|
-
true
|
497
|
-
when "N"
|
498
|
-
false
|
499
|
-
else
|
500
|
-
warn "Please enter Y or N"
|
501
|
-
pg_confirm_file_overwrite?(server, dump_file)
|
502
|
-
end
|
503
|
-
end
|
504
|
-
|
505
|
-
def pg_confirm_database_overwrite?(server, database)
|
506
|
-
warn "There is already data in #{database.name} on #{server.domain} in the container " +
|
507
|
-
"#{server.container_name} which stores it's data in #{server.data_path} on the host."
|
508
|
-
warn "If you continue, you must be positive you want to overwrite the existing data."
|
509
|
-
warn "Are you positive(Y/N)?"
|
510
|
-
STDOUT.flush
|
511
|
-
case STDIN.gets.chomp.upcase
|
512
|
-
when "Y"
|
513
|
-
true
|
514
|
-
when "N"
|
515
|
-
false
|
516
|
-
else
|
517
|
-
warn "Please enter Y or N"
|
518
|
-
pg_confirm_database_overwrite?(server, database)
|
519
|
-
end
|
520
|
-
end
|
521
|
-
|
522
|
-
def pg_database_empty?(cluster, server, database)
|
523
|
-
test("docker", "run", "--rm", "--volumes-from", server.container_name,
|
524
|
-
"--entrypoint", "/bin/bash", cluster.image.name, "-lc",
|
525
|
-
"'/usr/bin/psql", "-U", "postgres", "-d", database.name,
|
526
|
-
"-c", "\"\\dt\"", "|", "grep", "-qi", "\"no relations found\"'")
|
527
|
-
end
|
528
159
|
end
|