deployinator 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,75 @@
1
- namespace :config do
2
- set :stage, :staging
3
- desc 'Write deploy_example.rb and <stage>_example.rb files'
4
- task :write_examples do
5
- run_locally do
6
- execute "mkdir -p ./config/deploy"
1
+ namespace :deployinator do
2
+
3
+ # desc 'Ensure all deployinator specific settings are set, and warn and raise if not.'
4
+ # task :check_settings do
5
+ # run_locally do
6
+ # deploy_rb = File.expand_path("./config/deploy.rb")
7
+ # deploy_dir = File.expand_path("./config/deploy")
8
+ # {
9
+ # "Do not \"set :bundle_binstubs, ....\", deployinator will overwrite it and you'll be confused." =>
10
+ # system("grep -R -q -E '^set\ :bundle_binstubs' #{deploy_rb} #{deploy_dir}"),
11
+ # "Do not \"set :bundle_gemfile, ....\", deployinator will overwrite it and you'll be confused." =>
12
+ # system("grep -R -q -E '^set\ :bundle_gemfile' #{deploy_rb} #{deploy_dir}"),
13
+ # "You need \"set :domain, 'your.domain.com'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
14
+ # fetch(:domain).nil?,
15
+ # "You need \"set :nginx_container_name, 'your_nginx_container_name'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
16
+ # fetch(:nginx_container_name).nil?,
17
+ # "You need \"set :external_socket_path, 'your_external_socket_path'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
18
+ # fetch(:external_socket_path).nil?,
19
+ # "You need \"set :postgres_container_name, 'your_postgres_container_name'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
20
+ # fetch(:postgres_container_name).nil?,
21
+ # "You need \"set :postgres_port, 'your_postgres_port_number'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
22
+ # fetch(:postgres_port).nil?,
23
+ # "You need \"set :ruby_image_name, 'your_ruby_image_name'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
24
+ # fetch(:ruby_image_name).nil?,
25
+ # "You need \"set :ruby_container_name, 'your_ruby_container_name'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
26
+ # fetch(:ruby_container_name).nil?,
27
+ # "You need \"set :ruby_container_max_mem_mb, 'your_ruby_container_max_mem_mb'\" in your config/deploy/#{fetch(:stage)}.rb file." =>
28
+ # fetch(:ruby_container_max_mem_mb).nil?
29
+ # }.each do |message, true_false|
30
+ # fatal(message) and raise if true_false
31
+ # end
32
+ # end
33
+ # end
34
+ # before 'deploy:starting', 'deployinator:check_settings'
7
35
 
8
- # example deploy.rb
9
- config = "set :application, 'example-app'"
10
- File.open('./config/deploy_example.rb', 'w') { |f| f.write(config) }
36
+ # TODO make this better
37
+ task :check_templates do
38
+ run_locally do
39
+ keys_template = File.expand_path("./templates/deploy/deployer_authorized_keys.erb")
40
+ bluepill_template = File.expand_path("./templates/deploy/#{fetch(:application)}_bluepill.rb.erb")
41
+ unicorn_template = File.expand_path("./templates/deploy/#{fetch(:application)}_unicorn.rb.erb")
42
+ {
43
+ "You need a templates/deploy/deployer_authorized_keys.erb file." => keys_template,
44
+ "You need a templates/deploy/#{fetch(:application)}_bluepill.rb.erb file." => bluepill_template,
45
+ "You need a templates/deploy/#{fetch(:application)}_unicorn.rb.erb file." => unicorn_template,
46
+ }.each do |message, file|
47
+ fatal(message) and raise unless File.exists? file
48
+ end
49
+ end
50
+ end
51
+ before 'deploy:starting', 'deployinator:check_templates'
11
52
 
12
- # example <stage>.rb
13
- config = "set :example_attr, true"
14
- File.open('./config/deploy/stage_example.rb', 'w') { |f| f.write(config) }
53
+ desc 'Write example config files'
54
+ task :install do
55
+ run_locally do
56
+ execute "mkdir -p config/deploy templates/deploy"
57
+ {
58
+ "examples/Capfile" => "Capfile_example",
59
+ "examples/config/deploy.rb" => "config/deploy_example.rb",
60
+ "examples/config/deploy_deployinator.rb" => "config/deploy_deployinator_example.rb",
61
+ "examples/config/deploy/staging.rb" => "config/deploy/staging_example.rb",
62
+ "examples/config/deploy/staging_deployinator.rb" => "config/deploy/staging_deployinator_example.rb",
63
+ "examples/Dockerfile" => "templates/deploy/Dockerfile_example",
64
+ "examples/deployer_authorized_keys.erb" => "templates/deploy/deployer_authorized_keys_example.erb",
65
+ "examples/application_unicorn.rb.erb" => "templates/deploy/#{fetch(:application, "my_app")}_unicorn_example.rb.erb",
66
+ "examples/application_bluepill.rb.erb" => "templates/deploy/#{fetch(:application, "my_app")}_bluepill_example.rb.erb"
67
+ }.each do |source, destination|
68
+ config = File.read(File.dirname(__FILE__) + "/#{source}")
69
+ File.open("./#{destination}", 'w') { |f| f.write(config) }
70
+ info "Wrote '#{destination}'"
71
+ end
72
+ info "Now remove the '_example' portion of their names or diff with existing files and add the needed lines."
15
73
  end
16
74
  end
17
75
  end
@@ -1,119 +1,332 @@
1
1
  # config valid only for Capistrano 3.1
2
2
  lock '3.2.1'
3
3
 
4
- # Use `from_local=true bundle exec cap <stage> deploy` to deploy your
5
- # locally changed code instead of the code in the git repo.
6
- # TODO this is not working yet
7
- if ENV['from_local']
8
- set :repo_url, 'file://.'
9
- set :scm, :none
10
- else
11
- set :repo_url, 'git@github.com:snarlysodboxer/example.git'
12
- set :scm, :git
13
- end
4
+ namespace :deploy do
14
5
 
15
- # Default branch is :master
16
- # ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
6
+ task :set_bundle_command_map => [:set_deployer_user_id] do
7
+ SSHKit.config.command_map[:bundle] = [
8
+ "/usr/bin/env docker run --rm --tty",
9
+ "--user", fetch(:deployer_user_id),
10
+ "-e", "GEM_HOME=#{fetch(:deploy_to)}/shared/bundle",
11
+ "-e", "GEM_PATH=#{fetch(:deploy_to)}/shared/bundle",
12
+ "-e", "PATH=#{fetch(:deploy_to)}/shared/bundle/bin:$PATH",
13
+ "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/bundle",
14
+ "--volume #{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
15
+ fetch(:ruby_image_name)
16
+ ].join(' ')
17
+ end
18
+ before 'bundler:install', 'deploy:set_bundle_command_map'
17
19
 
18
- # Default deploy_to directory is /var/www/my_app
19
- # set :deploy_to, '/var/www/my_app'
20
+ task :set_rm_command_map do
21
+ SSHKit.config.command_map[:rm] = "/usr/bin/env sudo rm"
22
+ end
23
+ before 'deploy:started', 'deploy:set_rm_command_map'
20
24
 
21
- # Default value for :format is :pretty
22
- # set :format, :pretty
25
+ # Append dependancy to existing cleanup task
26
+ task :cleanup => :set_rm_command_map
23
27
 
24
- # Default value for :log_level is :debug
25
- #set :log_level, :debug
26
- set :log_level, :info
28
+ # Overwrite :assets:precompile to use docker
29
+ Rake::Task["deploy:assets:precompile"].clear
30
+ namespace :assets do
31
+ task :precompile do
32
+ on roles(fetch(:assets_roles)) do
33
+ execute(
34
+ "docker", "run", "--rm", "--tty",
35
+ "-w", fetch(:release_path, "#{fetch(:deploy_to)}/current"),
36
+ "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/rake",
37
+ "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
38
+ fetch(:ruby_image_name), "assets:precompile"
39
+ )
40
+ end
41
+ end
42
+ end
27
43
 
28
- # Default value for :pty is false
29
- # set :pty, true
44
+ # Overwrite :cleanup_assets to use docker
45
+ Rake::Task["deploy:cleanup_assets"].clear
46
+ desc 'Cleanup expired assets'
47
+ task :cleanup_assets => [:set_rails_env] do
48
+ on roles(fetch(:assets_roles)) do
49
+ execute(
50
+ "docker", "run", "--rm", "--tty",
51
+ "-e", "RAILS_ENV=#{fetch(:rails_env)}",
52
+ "-w", fetch(:release_path, "#{fetch(:deploy_to)}/current"),
53
+ "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/bundle",
54
+ "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
55
+ fetch(:ruby_image_name), "exec", "rake", "assets:clean"
56
+ )
57
+ end
58
+ end
30
59
 
31
- # Default value for :linked_files is []
32
- # set :linked_files, %w{config/nginx.conf}
60
+ # Overwrite :migrate to use docker
61
+ Rake::Task["deploy:migrate"].clear
62
+ desc 'Runs rake db:migrate if migrations are set'
63
+ task :migrate => [:set_rails_env, :ensure_running_postgres] do
64
+ on primary fetch(:migration_role) do
65
+ conditionally_migrate = fetch(:conditionally_migrate)
66
+ info '[deploy:migrate] Checking changes in /db/migrate' if conditionally_migrate
67
+ if conditionally_migrate && test("diff -q #{release_path}/db/migrate #{current_path}/db/migrate")
68
+ info '[deploy:migrate] Skip `deploy:migrate` (nothing changed in db/migrate)'
69
+ else
70
+ info '[deploy:migrate] Run `rake db:migrate`' if conditionally_migrate
71
+ execute(
72
+ "docker", "run", "--rm", "--tty",
73
+ "--link", "#{fetch(:postgres_container_name)}:postgres",
74
+ "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
75
+ "-e", "RAILS_ENV=#{fetch(:rails_env)}",
76
+ "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/rake",
77
+ "-w", fetch(:release_path, "#{fetch(:deploy_to)}/current"),
78
+ fetch(:ruby_image_name), "db:migrate"
79
+ )
80
+ end
81
+ end
82
+ end
33
83
 
34
- # Default value for linked_dirs is []
35
- # set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
84
+ # TODO make this better
85
+ task :ensure_running_postgres do
86
+ on primary fetch(:migration_role) do
87
+ unless capture("nc", "127.0.0.1", fetch(:postgres_port), "<", "/dev/null", ">", "/dev/null;", "echo", "$?").strip == "0"
88
+ fatal "Port #{fetch(:postgres_port)} is not responding, cannot db:migrate!"
89
+ raise
90
+ end
91
+ end
92
+ end
93
+ # task :ensure_running_postgres do
94
+ # on primary fetch(:migration_role) do
95
+ # if test "docker", "inspect", fetch(:postgres_container_name), "&>", "/dev/null"
96
+ # if (capture "docker", "inspect",
97
+ # "--format='{{.State.Running}}'",
98
+ # fetch(:postgres_container_name)).strip == "true"
99
+ # else
100
+ # execute "docker", "start", fetch(:postgres_container_name)
101
+ # end
102
+ # else
103
+ # set :run_pg_setup, ask("'true' or 'false', - #{fetch(:postgres_container_name)} is not running, would you like to run 'rake pg:setup'?", "false")
104
+ # if fetch(:run_pg_setup) == "true"
105
+ # #load Gem.bin_path('bundler', 'bundle')
106
+ # #import 'postgresinator'
107
+ # #load './infrastructure/Rakefile'
108
+ # Rake::Task['pg:setup'].invoke
109
+ # else
110
+ # raise "#{fetch(:postgres_container_name)} is not running, can't run db:migrate!"
111
+ # end
112
+ # end
113
+ # end
114
+ # end
115
+ before 'deploy:started', :ensure_running_postgres
36
116
 
37
- # Default value for default_env is {}
38
- # set :default_env, { path: "/opt/ruby/bin:$PATH" }
117
+ before 'deploy:check', :setup_deployer_user do
118
+ on "#{ENV['USER']}@#{fetch(:domain)}" do
119
+ as :root do
120
+ unless test "id", fetch(:deploy_username)
121
+ execute "adduser", "--disabled-password", "--gecos", "\"\"", fetch(:deploy_username)
122
+ execute "usermod", "-a", "-G", "sudo", fetch(:deploy_username)
123
+ execute "usermod", "-a", "-G", "docker", fetch(:deploy_username)
124
+ end
125
+ execute "mkdir", "-p", "/home/#{fetch(:deploy_username)}/.ssh"
126
+ # not actually using ERB interpolation, no need for an instance variable.
127
+ template_path = File.expand_path("./templates/deploy/#{fetch(:deploy_username)}_authorized_keys.erb")
128
+ generated_config_file = ERB.new(File.new(template_path).read).result(binding)
129
+ # upload! does not yet honor "as" and similar scoping methods
130
+ upload! StringIO.new(generated_config_file), "/tmp/authorized_keys"
131
+ execute "mv", "-b", "/tmp/authorized_keys", "/home/#{fetch(:deploy_username)}/.ssh/authorized_keys"
132
+ execute "chown", "-R", "#{fetch(:deploy_username)}:#{fetch(:deploy_username)}", "/home/#{fetch(:deploy_username)}/.ssh"
133
+ execute "chmod", "700", "/home/#{fetch(:deploy_username)}/.ssh"
134
+ execute "chmod", "600", "/home/#{fetch(:deploy_username)}/.ssh/authorized_keys"
135
+ end
136
+ end
137
+ end
39
138
 
40
- # Default value for keep_releases is 5
41
- # set :keep_releases, 5
139
+ task :ensure_www_data_user do
140
+ on "#{ENV['USER']}@#{fetch(:domain)}" do
141
+ as :root do
142
+ unless test "id", "www-data"
143
+ execute "adduser", "--disabled-password", "--gecos", "\"\"", "www-data"
144
+ end
145
+ end
146
+ end
147
+ end
148
+ after 'deploy:started', :ensure_www_data_user
42
149
 
43
- namespace :deploy do
150
+ task :set_deployer_user_id do
151
+ on roles(:app), in: :sequence, wait: 5 do
152
+ set :deployer_user_id, capture("id", "-u", fetch(:deploy_username)).strip
153
+ end
154
+ end
44
155
 
45
- # :started is the hook before which to set/check your needed environment/settings
46
- before :started, :ensure_setup
156
+ task :set_www_data_user_id do
157
+ on roles(:app), in: :sequence, wait: 5 do
158
+ set :www_data_user_id, capture("id", "-u", "www-data").strip
159
+ end
160
+ end
47
161
 
48
- task :ensure_setup do
49
- on roles(:web), :once => true do
50
- raise "You need to use ruby 1.9 or higher where you are running this capistrano command." unless RUBY_VERSION >= "1.9"
51
- test "bash", "-l", "-c", "'docker", "ps", "&>", "/dev/null", "||", "sudo", "usermod", "-a", "-G", "docker", "$USER'"
162
+ task :chown_log_dir => :set_www_data_user_id do
163
+ on roles(:app), in: :sequence, wait: 5 do
164
+ as :root do
165
+ unless test "[", "-d", "#{fetch(:deploy_to)}/shared/log", "]"
166
+ execute("mkdir", "-p", "#{fetch(:deploy_to)}/shared/log")
167
+ end
168
+ execute "chown", "-R", "#{fetch(:www_data_user_id)}:#{fetch(:www_data_user_id)}", "#{fetch(:deploy_to)}/shared/log"
169
+ end
52
170
  end
53
171
  end
172
+ before 'deploy:check:linked_dirs', :chown_log_dir
54
173
 
55
- # do this so capistrano has permissions to remove old releases
56
- before :updating, :revert_permissions do
57
- on roles(:web) do
58
- if test "[", "-d", "#{releases_path}", "]"
59
- execute "bash", "-l", "-c", "'sudo", "chown", "-R", "deployer.", "#{releases_path}'"
174
+ task :setup_deploy_to_dir => :set_deployer_user_id do
175
+ on roles(:app), in: :sequence, wait: 5 do
176
+ as :root do
177
+ [
178
+ fetch(:deploy_to),
179
+ "#{fetch(:deploy_to)}/shared",
180
+ "#{fetch(:deploy_to)}/releases"
181
+ ].each do |dir|
182
+ unless test "[", "-d", dir, "]"
183
+ execute("mkdir", "-p", dir)
184
+ end
185
+ execute "chown", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", dir
186
+ end
60
187
  end
61
188
  end
62
189
  end
190
+ after :setup_deployer_user, :setup_deploy_to_dir
63
191
 
64
- before :restart, :ensure_permissions do
65
- on roles(:web) do
66
- execute "bash", "-l", "-c", "'sudo", "mkdir", "-p", "#{current_path}/data/cache'"
67
- execute "bash", "-l", "-c", "'sudo", "chown", "-R", "www-data.", "#{current_path}/data'"
192
+ task :install_bundler => :set_deployer_user_id do
193
+ on roles(:app), in: :sequence, wait: 5 do
194
+ as :root do
195
+ unless test "[", "-f", "#{fetch(:deploy_to)}/shared/bundle/bin/bundle", "]"
196
+ execute(
197
+ "docker", "run", "--rm", "--tty",
198
+ "-e", "GEM_HOME=#{fetch(:deploy_to)}/shared/bundle",
199
+ "-e", "GEM_PATH=#{fetch(:deploy_to)}/shared/bundle",
200
+ "-e", "PATH=#{fetch(:deploy_to)}/shared/bundle/bin:$PATH",
201
+ "--entrypoint", "/usr/local/bin/gem",
202
+ "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
203
+ fetch(:ruby_image_name), "install",
204
+ "--install-dir", "#{fetch(:deploy_to)}/shared/bundle",
205
+ "--bindir", "#{fetch(:deploy_to)}/shared/bundle/bin",
206
+ "--no-ri", "--no-rdoc", "--quiet", "bundler", "-v'#{fetch(:bundler_version)}'"
207
+ )
208
+ end
209
+ execute "chown", "-R", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", "#{fetch(:deploy_to)}/shared/bundle"
210
+ end
68
211
  end
69
212
  end
213
+ before 'bundler:install', 'deploy:install_bundler'
70
214
 
71
- #desc 'Install config files in current_path'
72
- before :restart, :install_config_files do
73
- on roles(:web) do |host|
74
- require 'erb'
75
- { 'config/nginx.conf.erb' => "#{current_path}/config/#{fetch(:application)}-nginx.conf",
76
- 'config/php-fpm.conf.erb' => "#{current_path}/config/#{fetch(:application)}-php-fpm.conf",
77
- 'config/php.ini.erb' => "#{current_path}/config/#{fetch(:application)}-php.ini"
78
- }.each do |template, upload_path|
79
- @worker_processes = fetch(:worker_processes)
80
- template_path = File.expand_path(template)
81
- host_config = ERB.new(File.new(template_path).read).result(binding)
82
- upload! StringIO.new(host_config), upload_path
215
+ desc 'Restart application'
216
+ task :restart => [:set_www_data_user_id, :install_config_files] do
217
+ on roles(:app), in: :sequence, wait: 5 do
218
+ if test "docker", "inspect", fetch(:ruby_container_name), "&>", "/dev/null"
219
+ if (capture "docker", "inspect",
220
+ "--format='{{.State.Restarting}}'",
221
+ fetch(:ruby_container_name)).strip == "true"
222
+ execute("docker", "stop", fetch(:ruby_container_name))
223
+ execute("docker", "wait", fetch(:ruby_container_name))
224
+ end
225
+ if (capture "docker", "inspect",
226
+ "--format='{{.State.Running}}'",
227
+ fetch(:ruby_container_name)).strip == "true"
228
+ execute(
229
+ "docker", "exec", "--tty",
230
+ fetch(:ruby_container_name),
231
+ "#{fetch(:deploy_to)}/shared/bundle/bin/bluepill",
232
+ fetch(:application), "stop"
233
+ )
234
+ sleep 5
235
+ execute("docker", "stop", fetch(:ruby_container_name))
236
+ execute("docker", "wait", fetch(:ruby_container_name))
237
+ end
238
+ sleep 2
239
+ begin
240
+ execute("docker", "rm", fetch(:ruby_container_name))
241
+ rescue
242
+ sleep 5
243
+ begin
244
+ execute("docker", "rm", fetch(:ruby_container_name))
245
+ rescue
246
+ fatal "We were not able to remove the container for some reason. Try running 'cap <stage> deploy:restart' again."
247
+ end
248
+ end
249
+ end
250
+ as :root do
251
+ [
252
+ fetch(:external_socket_path),
253
+ "#{fetch(:deploy_to)}/current/public",
254
+ "#{fetch(:deploy_to)}/shared/tmp",
255
+ "#{fetch(:deploy_to)}/shared/log/production.log"
256
+ ].each do |directory|
257
+ execute "chown", "-R", "#{fetch(:www_data_user_id)}:#{fetch(:www_data_user_id)}", directory
258
+ end
259
+ execute "chown", "-R", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", "#{fetch(:deploy_to)}/shared/bundle"
83
260
  end
261
+ execute("docker", "run", fetch(:docker_run_bluepill_command))
84
262
  end
85
263
  end
264
+ after :publishing, :restart
86
265
 
87
- desc 'Restart or start application'
88
- task :restart do
89
- on roles(:web) do
90
- ['php5', 'nginx'].each do |name|
91
- container = {
92
- :name => fetch("#{name}_container_name".to_sym),
93
- :run_options => fetch("#{name}_run_options".to_sym).join(' '),
94
- :image => fetch("#{name}_image_name".to_sym)
95
- }
96
- exists = "docker inspect #{container[:name]} &> /dev/null"
97
- is_running = "docker inspect --format='{{.State.Running}}' #{container[:name]} 2>&1 | grep -q true"
98
- if test(exists)
99
- if test(is_running)
100
- execute "docker", "stop", container[:name]
266
+ after :publishing, :check_nginx_running do
267
+ on primary fetch(:migration_role) do
268
+ if test "docker", "inspect", fetch(:nginx_container_name), "&>", "/dev/null"
269
+ if (capture "docker", "inspect",
270
+ "--format='{{.State.Running}}'",
271
+ fetch(:nginx_container_name)).strip == "true"
272
+ else
273
+ warn "The nginx container named #{fetch(:nginx_container_name)} exists but is not running. (You need this, start it, or re-run setup with something like nginxinator.)"
274
+ end
275
+ else
276
+ warn "No nginx container named #{fetch(:nginx_container_name)} exists! (You need this, set it up with something like nginxinator.)"
277
+ end
278
+ end
279
+ end
280
+
281
+ after :publishing, :ensure_cadvisor do
282
+ on roles(:app), in: :sequence, wait: 5 do
283
+ if fetch(:use_cadvisor, true)
284
+ if test "docker", "inspect", "cadvisor", "&>", "/dev/null"
285
+ if (capture "docker", "inspect",
286
+ "--format='{{.State.Running}}'",
287
+ "cadvisor").strip == "true"
288
+ info "cadvisor is already running."
289
+ else
290
+ info "Restarting existing cadvisor container."
291
+ execute "docker", "start", "cadvisor"
101
292
  end
102
- execute "docker", "rm", container[:name]
293
+ else
294
+ execute("docker", "run", fetch(:docker_run_cadvisor_command))
295
+ end
296
+ end
297
+ end
298
+ end
299
+
300
+ after :restart, :clear_cache do
301
+ on roles(:web), in: :groups, limit: 3, wait: 10 do
302
+ # Here we can do anything such as:
303
+ # within release_path do
304
+ # execute :rake, 'cache:clear'
305
+ # end
306
+ end
307
+ end
308
+
309
+ task :install_config_files => [:set_deployer_user_id] do
310
+ on roles(:app), in: :sequence, wait: 5 do
311
+ set :bluepill_config, -> { "#{fetch(:application)}_bluepill.rb" }
312
+ set :unicorn_config, -> { "#{fetch(:application)}_unicorn.rb" }
313
+ set :socket_path, -> { fetch(:internal_socket_path) }
314
+ as 'root' do
315
+ [fetch(:bluepill_config), fetch(:unicorn_config)].each do |config_file|
316
+ @deploy_to = fetch(:deploy_to) # needed for ERB
317
+ @internal_socket_path = fetch(:socket_path) # needed for ERB
318
+ @application = fetch(:application) # needed for ERB
319
+ template_path = File.expand_path("./templates/deploy/#{config_file}.erb")
320
+ current_path = Pathname.new("#{fetch(:deploy_to)}/current")
321
+ generated_config_file = ERB.new(File.new(template_path).read).result(binding)
322
+ set :final_path, -> { fetch(:release_path, current_path).join('config', config_file) }
323
+ upload! StringIO.new(generated_config_file), "/tmp/#{config_file}"
324
+ execute("mv", "/tmp/#{config_file}", fetch(:final_path))
325
+ execute("chown", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", fetch(:final_path))
326
+ execute("chmod", "664", fetch(:final_path))
103
327
  end
104
- execute(
105
- "docker", "run",
106
- "--detach", "--tty",
107
- "--name", container[:name],
108
- container[:run_options],
109
- container[:image]
110
- )
111
- sleep 2
112
- error("Container #{container[:name]} did not stay running more than 2 seconds!") unless test(is_running)
113
328
  end
114
329
  end
115
330
  end
116
331
 
117
- # :publishing is the hook after which to set our service restart commands
118
- after :publishing, :restart
119
332
  end
@@ -0,0 +1,27 @@
1
+ # Load DSL and Setup Up Stages
2
+ require 'capistrano/setup'
3
+
4
+ # Includes default deployment tasks
5
+ require 'capistrano/deploy'
6
+
7
+ # Includes tasks from other gems included in your Gemfile
8
+ #
9
+ # For documentation on these, see for example:
10
+ #
11
+ # https://github.com/capistrano/rvm
12
+ # https://github.com/capistrano/rbenv
13
+ # https://github.com/capistrano/chruby
14
+ # https://github.com/capistrano/bundler
15
+ # https://github.com/capistrano/rails
16
+ #
17
+ # require 'capistrano/rvm'
18
+ # require 'capistrano/rbenv'
19
+ # require 'capistrano/chruby'
20
+ require 'capistrano/bundler'
21
+ require 'capistrano/rails/assets'
22
+ require 'capistrano/rails/migrations'
23
+
24
+ require 'deployinator'
25
+
26
+ # Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
27
+ Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
@@ -0,0 +1,16 @@
1
+ # vi: ft=config
2
+ FROM ubuntu:12.04
3
+ MAINTAINER david amick <docker@davidamick.com>
4
+
5
+ ENV DEBIAN_FRONTEND noninteractive
6
+
7
+ RUN /bin/bash -c "apt-get update -qq && apt-get install -qy wget curl bzip2 make gcc"
8
+ RUN wget -O ruby-install-0.5.0.tar.gz https://github.com/postmodern/ruby-install/archive/v0.5.0.tar.gz
9
+ RUN tar -xzvf ruby-install-0.5.0.tar.gz
10
+ RUN cd ruby-install-0.5.0/ && make install && ruby-install --system ruby 1.9.3-p547
11
+ RUN gem install bundler --no-ri --no-rdoc
12
+
13
+ RUN /bin/bash -c "apt-get update -qq && apt-get install -qy libxml2 libxml2-dev libxslt1-dev nodejs postgresql-contrib libpq-dev"
14
+
15
+ ENTRYPOINT ["/bin/bash"]
16
+ CMD ["-l"]
@@ -0,0 +1,27 @@
1
+ def red_unicorn_exec
2
+ "<%= @deploy_to %>/shared/bundle/bin/red_unicorn --env production --pid-file <%= @internal_socket_path %>/unicorn.pid --unicorn-exec <%= @deploy_to %>/shared/bundle/bin/unicorn_rails --unicorn-config <%= @deploy_to %>/current/config/<%= @application %>_unicorn.rb"
3
+ end
4
+
5
+ Bluepill.application("<%= @application %>", :log_file => "<%= @deploy_to %>/shared/log/bluepill.log", :foreground => true) do |app|
6
+ app.process("unicorn") do |process|
7
+ process.pid_file = "<%= @internal_socket_path %>/unicorn.pid"
8
+ process.working_dir = "<%= @deploy_to %>/current"
9
+ process.environment = {"BUNDLE_GEMFILE" => "<%= @deploy_to %>/current/Gemfile"}
10
+ process.start_command = "#{red_unicorn_exec} start"
11
+ process.stop_command = "#{red_unicorn_exec} stop"
12
+ process.restart_command = "'#{red_unicorn_exec} status; if [ $? = 0 ]; then #{red_unicorn_exec} restart; else #{red_unicorn_exec} start; fi'"
13
+
14
+ process.uid = process.gid = 'www-data'
15
+
16
+ process.start_grace_time = 30.seconds
17
+ process.stop_grace_time = 30.seconds
18
+ process.restart_grace_time = 60.seconds
19
+
20
+ process.monitor_children do |child_process|
21
+ child_process.stop_command = "kill -QUIT {{PID}}"
22
+
23
+ child_process.checks :mem_usage, :every => 30.seconds, :below => 300.megabytes, :times => [3,4], :fires => :stop
24
+ child_process.checks :cpu_usage, :every => 30.seconds, :below => 50, :times => [3,4], :fires => :stop
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ current_path = "<%= @deploy_to %>/current"
2
+ shared_path = "<%= @deploy_to %>/shared"
3
+ shared_bundler_gems_path = "<%= @deploy_to %>/shared/bundle"
4
+
5
+ worker_processes 4
6
+ working_directory current_path
7
+ listen "<%= @internal_socket_path %>/unicorn.socket", :backlog => 64
8
+ timeout 300
9
+ pid "<%= @internal_socket_path %>/unicorn.pid"
10
+
11
+ stderr_path "#{shared_path}/log/unicorn.stderr.log"
12
+ stdout_path "#{shared_path}/log/unicorn.stdout.log"
13
+
14
+ preload_app true
15
+ GC.respond_to?(:copy_on_write_friendly=) and
16
+ GC.copy_on_write_friendly = true
17
+
18
+ before_fork do |server, worker|
19
+ defined?(ActiveRecord::Base) and
20
+ ActiveRecord::Base.connection.disconnect!
21
+ end
22
+
23
+ after_fork do |server, worker|
24
+ worker.user('www-data', 'www-data') if Process.euid == 0
25
+
26
+ defined?(ActiveRecord::Base) and
27
+ ActiveRecord::Base.establish_connection
28
+ end
29
+
30
+ before_exec do |server|
31
+ # Configure bundler to use proper paths if app provides a Gemfile
32
+ if(File.exists?("#{current_path}/Gemfile"))
33
+ paths = ENV["PATH"].to_s.split(':')
34
+ paths.unshift "#{shared_bundler_gems_path}/bin"
35
+ ENV["PATH"] = paths.uniq.join(':')
36
+
37
+ ENV['GEM_HOME'] = ENV['GEM_PATH'] = shared_bundler_gems_path
38
+ ENV['BUNDLE_GEMFILE'] = "#{current_path}/Gemfile"
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ # Simple Role Syntax
2
+ # ==================
3
+ # Supports bulk-adding hosts to roles, the primary server in each group
4
+ # is considered to be the first unless any hosts have the primary
5
+ # property set. Don't declare `role :all`, it's a meta role.
6
+
7
+ set :domain, "my-app-staging.example.com"
8
+ set :deploy_username, "deployer"
9
+ set :user_host, "#{fetch(:deploy_username)}@#{fetch(:domain)}"
10
+
11
+ role :app, fetch(:user_host)
12
+ role :web, fetch(:user_host)
13
+ role :db, fetch(:user_host)
14
+
15
+ # Extended Server Syntax
16
+ # ======================
17
+ # This can be used to drop a more detailed server definition into the
18
+ # server list. The second argument is a, or duck-types, Hash and is
19
+ # used to set extended properties on the server.
20
+
21
+ #server 'example.com', user: 'deploy', roles: %w{web app}, my_property: :my_value
22
+
23
+
24
+ # Custom SSH Options
25
+ # ==================
26
+ # You may pass any option but keep in mind that net/ssh understands a
27
+ # limited set of options, consult[net/ssh documentation](http://net-ssh.github.io/net-ssh/classes/Net/SSH.html#method-c-start).
28
+ #
29
+ # Global options
30
+ # --------------
31
+ # set :ssh_options, {
32
+ # keys: %w(/home/rlisowski/.ssh/id_rsa),
33
+ # forward_agent: false,
34
+ # auth_methods: %w(password)
35
+ # }
36
+ #
37
+ # And/or per server (overrides global)
38
+ # ------------------------------------
39
+ # server 'example.com',
40
+ # user: 'user_name',
41
+ # roles: %w{web app},
42
+ # ssh_options: {
43
+ # user: 'user_name', # overrides user setting above
44
+ # keys: %w(/home/user_name/.ssh/id_rsa),
45
+ # forward_agent: false,
46
+ # auth_methods: %w(publickey password)
47
+ # # password: 'please use keys'
48
+ # }
49
+
50
+ require './config/deploy/staging_deployinator.rb'
@@ -0,0 +1,17 @@
1
+ set :rails_env, 'production' # If the environment differs from the stage name
2
+ set :migration_role, 'app' # Defaults to 'db'
3
+ #set :conditionally_migrate, true # Defaults to false. If true, it's skip migration if files in db/migrate not modified
4
+ set :assets_roles, [:app] # Defaults to [:web]
5
+ #set :assets_prefix, 'prepackaged-assets' # Defaults to 'assets' this should match config.assets.prefix in your rails config/application.rb
6
+
7
+ # If you are using nginxinator and postgresinator, your settings would look similar to this:
8
+ set :nginx_container_name, "my-app-staging.example.com-nginx-80-443"
9
+ set :external_socket_path, "/my-app-staging.example.com-nginx-80-443-conf/run"
10
+ set :internal_socket_path, "/var/run/unicorn"
11
+ set :postgres_container_name, "my-app-staging.example.com-postgres-5432-master"
12
+ set :postgres_port, "5432"
13
+ set :ruby_image_name, "snarlysodboxer/ruby:1.9.3-p547"
14
+ set :ruby_container_name, "my-app-staging.example.com-ruby-bluepill"
15
+ set :ruby_container_max_mem_mb, "1024"
16
+ set :bundler_version, "1.7.4"
17
+ #set :use_cadvisor, true # this is default
@@ -0,0 +1,63 @@
1
+ # config valid only for Capistrano 3.1
2
+ lock '3.2.1'
3
+
4
+ set :application, 'my_app_name'
5
+ set :repo_url, 'git@example.com:me/my_repo.git'
6
+
7
+ ## Don't ask this here, only in staging or other non-production <stage>.rb files.
8
+ # Default branch is :master
9
+ # ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
10
+
11
+ # Default deploy_to directory is /var/www/my_app
12
+ # set :deploy_to, '/var/www/my_app'
13
+
14
+ # Default value for :scm is :git
15
+ # set :scm, :git
16
+
17
+ # Default value for :format is :pretty
18
+ # set :format, :pretty
19
+
20
+ # Default value for :log_level is :debug
21
+ # set :log_level, :debug
22
+
23
+ # Default value for :pty is false
24
+ # set :pty, true
25
+
26
+ # Default value for :linked_files is []
27
+ # set :linked_files, %w{config/database.yml}
28
+
29
+ # Default value for linked_dirs is []
30
+ # set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
31
+ set :linked_dirs, %w{log tmp}
32
+
33
+ # Default value for default_env is {}
34
+ # set :default_env, { path: "/opt/ruby/bin:$PATH" }
35
+
36
+ # Default value for keep_releases is 5
37
+ # set :keep_releases, 5
38
+
39
+ namespace :deploy do
40
+
41
+ ### No need to do anything here, deployinator defines this task.
42
+ desc 'Restart application'
43
+ task :restart do
44
+ on roles(:app), in: :sequence, wait: 5 do
45
+ # Your restart mechanism here, for example:
46
+ # execute :touch, release_path.join('tmp/restart.txt')
47
+ end
48
+ end
49
+
50
+ after :publishing, :restart
51
+
52
+ after :restart, :clear_cache do
53
+ on roles(:web), in: :groups, limit: 3, wait: 10 do
54
+ # Here we can do anything such as:
55
+ # within release_path do
56
+ # execute :rake, 'cache:clear'
57
+ # end
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ require './config/deploy_deployinator.rb'
@@ -0,0 +1,56 @@
1
+ # Use `cap <stage> deploy from_local=true` to deploy your
2
+ # locally changed code instead of the code in the git repo. You can also add --trace.
3
+ if ENV['from_local']
4
+ set :scm, :copy
5
+ else
6
+ set :scm, :git
7
+ end
8
+
9
+ # Always use the master branch in production:
10
+ set :current_stage, -> { fetch(:stage).to_s.strip }
11
+ unless fetch(:current_stage) == "production"
12
+ ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
13
+ end
14
+
15
+ ## The values in this file are not meant to be changed and shouldn't
16
+ ## need to be under the majority of circumstances:
17
+
18
+ #set :bundle_roles, :all # this is default
19
+ #set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } # this is default
20
+ #set :bundle_binstubs, -> { shared_path.join('bin') } # this is default
21
+ set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') } # this is required for deployinator
22
+ set :bundle_gemfile, -> { release_path.join('Gemfile') } # this is required for deployinator
23
+ #set :bundle_path, -> { shared_path.join('bundle') } # this is default
24
+ #set :bundle_without, %w{development test}.join(' ') # this is default
25
+ set :bundle_without, %w{development test deployment}.join(' ')
26
+ #set :bundle_flags, '--deployment --quiet' # this is default
27
+ #set :bundle_flags, '--deployment'
28
+ #set :bundle_env_variables, {} # this is default
29
+
30
+ set :docker_run_bluepill_command, -> { [
31
+ "--tty", "--detach",
32
+ "--name", fetch(:ruby_container_name),
33
+ "-e", "GEM_HOME=#{fetch(:deploy_to)}/shared/bundle",
34
+ "-e", "GEM_PATH=#{fetch(:deploy_to)}/shared/bundle",
35
+ "-e", "BUNDLE_GEMFILE=#{fetch(:deploy_to)}/current/Gemfile",
36
+ "-e", "PATH=#{fetch(:deploy_to)}/shared/bundle/bin:$PATH",
37
+ "--link", "#{fetch(:postgres_container_name)}:postgres",
38
+ "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
39
+ "--volume", "#{fetch(:external_socket_path)}:#{fetch(:internal_socket_path)}:rw",
40
+ "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/bluepill",
41
+ "--restart", "always", "--memory", "#{fetch(:ruby_container_max_mem_mb)}m",
42
+ fetch(:ruby_image_name), "load",
43
+ "#{fetch(:deploy_to)}/current/config/#{fetch(:application)}_bluepill.rb"
44
+ ] }
45
+
46
+ set :docker_run_cadvisor_command, -> { [
47
+ "--detach",
48
+ "--name", "cadvisor",
49
+ "--volume", "/:/rootfs:ro",
50
+ "--volume", "/var/run:/var/run:rw",
51
+ "--volume", "/sys:/sys:ro",
52
+ "--volume", "/var/lib/docker/:/var/lib/docker:ro",
53
+ "--publish", "127.0.0.1:8080:8080",
54
+ "--restart", "always",
55
+ "google/cadvisor:latest"
56
+ ] }
@@ -0,0 +1 @@
1
+ # Add your keys here:
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deployinator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - david amick
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-09-11 00:00:00.000000000 Z
12
+ date: 2014-11-10 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: capistrano
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - '='
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - '='
25
28
  - !ruby/object:Gem::Version
@@ -33,28 +36,38 @@ files:
33
36
  - lib/deployinator.rb
34
37
  - lib/deployinator/deploy.rb
35
38
  - lib/deployinator/config.rb
36
- homepage: http://rubygems.org/gems/deployinator
39
+ - lib/deployinator/examples/Capfile
40
+ - lib/deployinator/examples/config/deploy.rb
41
+ - lib/deployinator/examples/config/deploy_deployinator.rb
42
+ - lib/deployinator/examples/config/deploy/staging.rb
43
+ - lib/deployinator/examples/config/deploy/staging_deployinator.rb
44
+ - lib/deployinator/examples/Dockerfile
45
+ - lib/deployinator/examples/deployer_authorized_keys.erb
46
+ - lib/deployinator/examples/application_unicorn.rb.erb
47
+ - lib/deployinator/examples/application_bluepill.rb.erb
48
+ homepage: https://github.com/snarlysodboxer/deployinator
37
49
  licenses:
38
- - MIT
39
- metadata: {}
50
+ - GNU
40
51
  post_install_message:
41
52
  rdoc_options: []
42
53
  require_paths:
43
54
  - lib
44
55
  required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
45
57
  requirements:
46
- - - '>='
58
+ - - ! '>='
47
59
  - !ruby/object:Gem::Version
48
60
  version: '0'
49
61
  required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
50
63
  requirements:
51
- - - '>='
64
+ - - ! '>='
52
65
  - !ruby/object:Gem::Version
53
66
  version: '0'
54
67
  requirements: []
55
68
  rubyforge_project:
56
- rubygems_version: 2.0.2
69
+ rubygems_version: 1.8.23.2
57
70
  signing_key:
58
- specification_version: 4
71
+ specification_version: 3
59
72
  summary: Deploy Applications
60
73
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 1118ca9720569f14d4453a2d96561b65395362f8
4
- data.tar.gz: 1d9079e7d2e1a79ec2a612f2c47a0d14355a017a
5
- SHA512:
6
- metadata.gz: a6fa60095a8adaa33144331cade009a5493def8f046f0d7d49c95bab97c0ecf686fce20c85efca467f67b79372b884ea9017b9e1aeaaab5ffc38b6a2a980457c
7
- data.tar.gz: 9b8bf3e88297d083a9ef13de344a5b76455a23cf277a1d07c2a3fc961563eb148fedecd4fd9b0abc9744637fef53d7eca112424d978c51633a73b551e8c4874e