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