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.
@@ -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 => ['config:load_settings', 'config:ensure_cluster_data_uniqueness'] do |t, args|
13
- # use 'rake pg:COMMAND debug=true' for debugging (you can also add --trace if you like)
14
- SSHKit.config.output_verbosity = Logger::DEBUG if ENV['debug'] == "true"
15
- Rake::Task['config:config_file_not_found'].invoke(args.config_file) unless args.config_file.nil?
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 using values in ./postgresinator.rb"
22
- task :setup => :ensure_setup do
23
- # instance variables are lost inside SSHKit's 'on' block, so
24
- # at the beginning of each task we assign cluster to @cluster.
25
- cluster = @cluster
26
- cluster.servers.each do |server|
27
- Rake::Task['pg:ensure_access_docker'].invoke(server.domain)
28
- Rake::Task['pg:ensure_access_docker'].reenable
29
- Rake::Task['pg:open_firewall'].invoke(server.domain)
30
- Rake::Task['pg:open_firewall'].reenable
31
- # 'on', 'run_locally', 'as', 'execute', 'info', 'warn', and 'fatal' are from SSHKit
32
- on "#{cluster.ssh_user}@#{server.domain}" do
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
- end
87
- end
88
-
89
- desc "Check the statuses of each PostgreSQL instance in the cluster."
90
- task :statuses => :ensure_setup do
91
- cluster = @cluster
92
- cluster.servers.each do |server|
93
- Rake::Task['pg:status'].invoke(server.domain)
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
- end
125
-
126
- desc "Restore 'dump_file' in /tmp on the master server into 'database_name'."
127
- task :restore, [:dump_file, :database_name] => :ensure_setup do |t, args|
128
- cluster = @cluster
129
- database = cluster.databases.select { |d| d.name == args.database_name }.first
130
- # we only restore on the master server
131
- server = cluster.servers.select { |s| s.master }.first
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
- execute("docker", "run", "--rm",
139
- "--volume", "/tmp:/tmp:rw",
140
- "--entrypoint", "/bin/bash",
141
- "--volumes-from", server.container_name,
142
- cluster.image.name,
143
- "-c", "'/usr/bin/pg_restore", "-U", "postgres", clean,
144
- "-d", database.name, "-F", "tar", "-v", "/tmp/#{args.dump_file}'"
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 "Print the command to enter psql interactive mode on 'domain'."
172
- task :print_interactive, [:domain] => :ensure_setup do |t, args|
173
- cluster = @cluster
174
- server = cluster.servers.select { |s| s.domain == args.domain }.first
175
- run_locally do
176
- info "You can paste the following command into a terminal on #{server.domain} to enter psql interactive mode for #{server.container_name}:"
177
- info "docker run --rm --interactive --tty --volumes-from #{server.container_name} --entrypoint /bin/bash #{cluster.image.name} -lic '/usr/bin/psql -U postgres'"
178
- end
179
- end
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 :create_container, [:domain] => :ensure_config_files do |t, args|
196
- cluster = @cluster
197
- server = cluster.servers.select { |s| s.domain == args.domain }.first
198
- master_server = cluster.servers.select { |s| s.master }.first
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
- fatal_message = "#{server.data_path} on #{server.domain} is not empty, cannot continue! " +
208
- "You'll need to delete those files by hand. Be sure you are not deleting important data!"
209
- fatal fatal_message and raise if pg_files_in_data_path?(server)
210
- execute("mkdir", "-p", server.data_path) unless test("test", "-d", server.data_path)
211
- execute("chown", "-R", "#{cluster.image.postgres_uid}:#{cluster.image.postgres_gid}", server.data_path)
212
- execute("chmod", "700", server.data_path)
213
- execute("docker", "run", server.docker_init_command)
214
- unless server.master
215
- execute("rm", "#{server.data_path}/*", "-rf")
216
- execute("docker", "run", server.docker_replicate_command)
217
- Rake::Task['pg:link_keys'].invoke(server.domain)
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
- task :restart_container, [:domain] => :ensure_setup do |t, args|
228
- cluster = @cluster
229
- server = cluster.servers.select { |s| s.domain == args.domain }.first
230
- on "#{cluster.ssh_user}@#{server.domain}" do
231
- warn "Restarting a running container named #{server.container_name}"
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
- end
259
- end
260
-
261
- task :install_config_file, [:domain, :config_file] => :ensure_setup do |t, args|
262
- cluster = @cluster
263
- server = cluster.servers.select { |s| s.domain == args.domain }.first
264
- on "#{cluster.ssh_user}@#{args.domain}" do
265
- as 'root' do
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
- task :list_roles, [:domain] => :ensure_setup do |t, args|
279
- cluster = @cluster
280
- server = cluster.servers.select { |s| s.domain == args.domain }.first
281
- on "#{cluster.ssh_user}@#{args.domain}" do
282
- capture("docker", "run", "--rm",
283
- "--entrypoint", "/bin/bash",
284
- "--volumes-from", server.container_name,
285
- cluster.image.name,
286
- "-c", "'/usr/bin/psql", "-U", "postgres",
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
- task :streaming_status, [:domain] => :ensure_setup do |t, args|
305
- cluster = @cluster
306
- server = cluster.servers.select { |s| s.domain == args.domain }.first
307
- on "#{cluster.ssh_user}@#{args.domain}" do
308
- if server.master
309
- info "Streaming status of #{server.container_name} on #{server.domain}:"
310
- capture("echo", "\"SELECT", "*", "FROM", "pg_stat_replication;\"", "|",
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
- # TODO: fix this for slave servers
318
- info "Streaming status of #{server.container_name} on #{server.domain}:"
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
- task :create_role, [:domain, :role_name, :force] => :ensure_setup do |t, args|
331
- args.with_defaults :force => "false"
332
- cluster = @cluster
333
- server = cluster.servers.select { |s| s.domain == args.domain }.first
334
- on "#{cluster.ssh_user}@#{args.domain}" do
335
- execute("echo", "\"CREATE", "ROLE", "\\\"#{args.role_name}\\\"",
336
- "WITH", "LOGIN", "ENCRYPTED", "PASSWORD", "'#{args.role_name}'",
337
- "REPLICATION", "CREATEDB;\"", "|",
338
- "docker", "run", "--rm", "--interactive",
339
- "--entrypoint", "/bin/bash",
340
- "--volumes-from", server.container_name,
341
- cluster.image.name,
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