deployinator 0.0.7 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/deployinator.rb CHANGED
@@ -2,4 +2,6 @@ require 'capistrano/setup'
2
2
  require 'capistrano/deploy'
3
3
 
4
4
  load 'deployinator/deploy.rb'
5
+ load 'deployinator/check.rb'
5
6
  load 'deployinator/config.rb'
7
+ load 'deployinator/helpers.rb'
@@ -0,0 +1,133 @@
1
+ namespace :deploy do
2
+ namespace :check do
3
+
4
+ task :bundle_command_map => ['deployinator:deployment_user'] do
5
+ set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') }
6
+ set :bundle_gemfile, -> { release_path.join('Gemfile') }
7
+ SSHKit.config.command_map[:bundle] = [
8
+ "/usr/bin/env docker run --rm --tty",
9
+ "--user", fetch(:deployment_user_id),
10
+ "-e", "GEM_HOME=#{shared_path.join('bundle')}",
11
+ "-e", "GEM_PATH=#{shared_path.join('bundle')}",
12
+ "-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
13
+ "--entrypoint", "#{shared_path.join('bundle', 'bin', 'bundle')}",
14
+ "--volume $SSH_AUTH_SOCK:/ssh-agent --env SSH_AUTH_SOCK=/ssh-agent",
15
+ "--volume #{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
16
+ "--volume /etc/passwd:/etc/passwd:ro",
17
+ "--volume /etc/group:/etc/group:ro",
18
+ "--volume /home:/home:rw",
19
+ fetch(:ruby_image_name)
20
+ ].join(' ')
21
+ end
22
+ before 'bundler:install', 'deploy:check:bundle_command_map'
23
+
24
+ # Ensure Capistrano's inner rm commands always run using sudo
25
+ before 'deploy:started', :rm_command_map do
26
+ SSHKit.config.command_map[:rm] = "/usr/bin/env sudo rm"
27
+ end
28
+
29
+ if Rake::Task.task_defined?("deploy:cleanup")
30
+ # Append dependancy to existing cleanup task
31
+ task 'deploy:cleanup' => 'deploy:check:rm_command_map'
32
+ end
33
+
34
+ desc 'Ensure all deployinator specific settings are set, and warn and raise if not.'
35
+ task :settings do
36
+ {
37
+ (File.dirname(__FILE__) + "/examples/config/deploy.rb") => 'config/deploy.rb',
38
+ (File.dirname(__FILE__) + "/examples/config/deploy/staging.rb") => "config/deploy/#{fetch(:stage)}.rb"
39
+ }.each do |abs, rel|
40
+ Rake::Task['deployinator:settings'].invoke(abs, rel)
41
+ Rake::Task['deployinator:settings'].reenable
42
+ end
43
+ end
44
+
45
+ # TODO make this better
46
+ before 'deploy:check', :templates do
47
+ run_locally do
48
+ keys_template = File.expand_path("./templates/deploy/deployment_authorized_keys.erb")
49
+ bluepill_template = File.expand_path("./templates/deploy/bluepill.rb.erb")
50
+ unicorn_template = File.expand_path("./templates/deploy/unicorn.rb.erb")
51
+ {
52
+ "You need a templates/deploy/deployment_authorized_keys.erb file." => keys_template,
53
+ "You need a templates/deploy/bluepill.rb.erb file." => bluepill_template,
54
+ "You need a templates/deploy/unicorn.rb.erb file." => unicorn_template,
55
+ }.each do |message, file|
56
+ fatal(message) and raise unless File.exists? file
57
+ end
58
+ end
59
+ end
60
+
61
+ before 'deploy:check', 'deployinator:deployment_user'
62
+ before 'deploy:check', 'deployinator:webserver_user'
63
+
64
+ task :root_dir_permissions => ['deployinator:deployment_user', 'deployinator:webserver_user'] do
65
+ on roles(:app) do
66
+ as :root do
67
+ [fetch(:deploy_to), Pathname.new(fetch(:deploy_to)).join("../")].each do |dir|
68
+ if directory_exists?(dir)
69
+ execute "chown", "#{fetch(:deployment_user_id)}:#{fetch(:webserver_user_id)}", dir
70
+ execute "chmod", "2750", dir
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ before 'deploy:check:directories', 'deploy:check:root_dir_permissions'
77
+
78
+ before 'deploy:check', :webserver_running do
79
+ on roles(:app) do
80
+ if container_exists?(fetch(:webserver_container_name))
81
+ unless container_is_running?(fetch(:webserver_container_name))
82
+ warn "The webserver container named #{fetch(:webserver_container_name)} exists but is not running. You can still deploy your code, but you need this, start it, or re-run setup with something like nginxinator."
83
+ ask :return_to_continue, nil
84
+ set :nothing, fetch(:return_to_continue)
85
+ end
86
+ else
87
+ warn "No webserver container named #{fetch(:webserver_container_name)} exists! You can still deploy your code, but you need this, set it up with something like nginxinator."
88
+ ask :return_to_continue, nil
89
+ set :nothing, fetch(:return_to_continue)
90
+ end
91
+ end
92
+ end
93
+
94
+ before 'deploy:check', :postgres_running do
95
+ on primary fetch(:migration_role) do
96
+ if container_exists?(fetch(:postgres_container_name))
97
+ if container_is_running?(fetch(:postgres_container_name))
98
+ unless localhost_port_responding?(fetch(:postgres_port))
99
+ fatal "Port #{fetch(:postgres_port)} is not responding, we won't be able to db:migrate!"
100
+ raise
101
+ end
102
+ else
103
+ fatal "#{fetch(:postgres_container_name)} exists but is not running, we won't be able to db:migrate!"
104
+ raise
105
+ end
106
+ else
107
+ fatal "#{fetch(:postgres_container_name)} does not exist, we won't be able to db:migrate!"
108
+ raise
109
+ end
110
+ end
111
+ end
112
+
113
+ before 'deploy:check', :ensure_cadvisor do
114
+ on roles(:app), in: :sequence, wait: 5 do
115
+ if fetch(:use_cadvisor, true)
116
+ if container_exists?("cadvisor")
117
+ if container_is_running?("cadvisor")
118
+ info "cadvisor is already running."
119
+ else
120
+ info "Restarting existing cadvisor container."
121
+ execute "docker", "start", "cadvisor"
122
+ end
123
+ else
124
+ execute("docker", "run", fetch(:docker_run_cadvisor_command))
125
+ end
126
+ else
127
+ info "Not using cadvisor."
128
+ end
129
+ end
130
+ end
131
+
132
+ end
133
+ end
@@ -1,69 +1,17 @@
1
1
  namespace :deployinator do
2
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'
35
-
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'
52
-
53
3
  desc 'Write example config files'
54
4
  task :write_example_configs do
55
5
  run_locally do
56
6
  execute "mkdir", "-p", "config/deploy", "templates/deploy"
57
7
  {
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"
8
+ "examples/Capfile" => "Capfile_example",
9
+ "examples/config/deploy.rb" => "config/deploy_example.rb",
10
+ "examples/config/deploy/staging.rb" => "config/deploy/staging_example.rb",
11
+ "examples/Dockerfile" => "templates/deploy/Dockerfile_example",
12
+ "examples/deployment_authorized_keys.erb" => "templates/deploy/deployment_authorized_keys_example.erb",
13
+ "examples/unicorn.rb.erb" => "templates/deploy/unicorn_example.rb.erb",
14
+ "examples/bluepill.rb.erb" => "templates/deploy/bluepill_example.rb.erb"
67
15
  }.each do |source, destination|
68
16
  config = File.read(File.dirname(__FILE__) + "/#{source}")
69
17
  File.open("./#{destination}", 'w') { |f| f.write(config) }
@@ -72,4 +20,5 @@ namespace :deployinator do
72
20
  info "Now remove the '_example' portion of their names or diff with existing files and add the needed lines."
73
21
  end
74
22
  end
23
+
75
24
  end
@@ -3,67 +3,41 @@ lock '3.2.1'
3
3
 
4
4
  namespace :deploy do
5
5
 
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 $SSH_AUTH_SOCK:/ssh-agent --env SSH_AUTH_SOCK=/ssh-agent",
15
- "--volume #{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
16
- "--volume /etc/passwd:/etc/passwd:ro",
17
- "--volume /etc/group:/etc/group:ro",
18
- "--volume /home:/home:rw",
19
- fetch(:ruby_image_name)
20
- ].join(' ')
21
- end
22
- before 'bundler:install', 'deploy:set_bundle_command_map'
23
-
24
- task :set_rm_command_map do
25
- SSHKit.config.command_map[:rm] = "/usr/bin/env sudo rm"
26
- end
27
- before 'deploy:started', 'deploy:set_rm_command_map'
28
-
29
- # Append dependancy to existing cleanup task
30
- task :cleanup => :set_rm_command_map
31
-
32
- if Rake::Task.task_defined?("bundler:install")
33
- #desc 'Copies .git folder'
34
- task :copy_git do
35
- unless ENV['from_local'] == "true"
36
- on roles(:app) do
37
- within release_path do
38
- execute :cp, '-r', repo_path, '.git'
39
- end
6
+ #desc 'Copies .git folder to support .gemspecs that run git commands'
7
+ task :copy_git do
8
+ unless ENV['from_local'] == "true"
9
+ on roles(:app) do
10
+ within release_path do
11
+ execute :cp, '-r', repo_path, '.git'
40
12
  end
41
- end
13
+ end
42
14
  end
43
- before 'bundler:install', 'deploy:copy_git'
44
15
  end
16
+ before 'bundler:install', 'deploy:copy_git'
45
17
 
46
- # If defined, overwrite :assets:precompile to use docker
47
18
  if Rake::Task.task_defined?("deploy:assets:precompile")
19
+ # Overwrite :assets:precompile to use docker
48
20
  Rake::Task["deploy:assets:precompile"].clear
49
21
  namespace :assets do
50
- task :precompile => :set_deployer_user_id do
22
+ task :precompile => ['deployinator:deployment_user'] do
51
23
  on roles(fetch(:assets_roles)) do
52
24
  execute(
53
- "docker", "run", "--rm", "--tty", "--user", fetch(:deployer_user_id),
54
- "-w", fetch(:release_path, "#{fetch(:deploy_to)}/current"),
25
+ "docker", "run", "--rm", "--tty", "--user", fetch(:deployment_user_id),
26
+ "-w", release_path,
55
27
  "--link", "#{fetch(:postgres_container_name)}:postgres",
56
- "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/rake",
28
+ "--entrypoint", "/bin/bash",
57
29
  "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
58
- fetch(:ruby_image_name), "assets:precompile"
30
+ fetch(:ruby_image_name), "-c",
31
+ "\"umask", "0007", "&&", "#{shared_path.join('bundle', 'bin', 'rake')}",
32
+ "assets:precompile\""
59
33
  )
60
34
  end
61
35
  end
62
36
  end
63
37
  end
64
38
 
65
- # If defined, overwrite :cleanup_assets to use docker
66
39
  if Rake::Task.task_defined?("deploy:cleanup_assets")
40
+ # Overwrite :cleanup_assets to use docker
67
41
  Rake::Task["deploy:cleanup_assets"].clear
68
42
  desc 'Cleanup expired assets'
69
43
  task :cleanup_assets => [:set_rails_env] do
@@ -71,8 +45,8 @@ namespace :deploy do
71
45
  execute(
72
46
  "docker", "run", "--rm", "--tty",
73
47
  "-e", "RAILS_ENV=#{fetch(:rails_env)}",
74
- "-w", fetch(:release_path, "#{fetch(:deploy_to)}/current"),
75
- "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/bundle",
48
+ "-w", release_path,
49
+ "--entrypoint", shared_path.join('bundle', 'bin', 'bundle'),
76
50
  "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
77
51
  fetch(:ruby_image_name), "exec", "rake", "assets:clean"
78
52
  )
@@ -80,11 +54,11 @@ namespace :deploy do
80
54
  end
81
55
  end
82
56
 
83
- # If defined, overwrite :migrate to use docker
84
57
  if Rake::Task.task_defined?("deploy:migrate")
58
+ # Overwrite :migrate to use docker
85
59
  Rake::Task["deploy:migrate"].clear
86
60
  desc 'Runs rake db:migrate if migrations are set'
87
- task :migrate => [:set_rails_env, :ensure_running_postgres] do
61
+ task :migrate => [:set_rails_env, 'deploy:check:postgres_running'] do
88
62
  on primary fetch(:migration_role) do
89
63
  conditionally_migrate = fetch(:conditionally_migrate)
90
64
  info '[deploy:migrate] Checking changes in /db/migrate' if conditionally_migrate
@@ -97,8 +71,8 @@ namespace :deploy do
97
71
  "--link", "#{fetch(:postgres_container_name)}:postgres",
98
72
  "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
99
73
  "-e", "RAILS_ENV=#{fetch(:rails_env)}",
100
- "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/rake",
101
- "-w", fetch(:release_path, "#{fetch(:deploy_to)}/current"),
74
+ "--entrypoint", shared_path.join('bundle', 'bin', 'rake'),
75
+ "-w", release_path,
102
76
  fetch(:ruby_image_name), "db:migrate"
103
77
  )
104
78
  end
@@ -106,168 +80,47 @@ namespace :deploy do
106
80
  end
107
81
  end
108
82
 
109
- # TODO make this better
110
- task :ensure_running_postgres do
111
- on primary fetch(:migration_role) do
112
- unless capture("nc", "127.0.0.1", fetch(:postgres_port), "<", "/dev/null", ">", "/dev/null;", "echo", "$?").strip == "0"
113
- fatal "Port #{fetch(:postgres_port)} is not responding, cannot db:migrate!"
114
- raise
115
- end
116
- end
117
- end
118
- # task :ensure_running_postgres do
119
- # on primary fetch(:migration_role) do
120
- # if test "docker", "inspect", fetch(:postgres_container_name), "&>", "/dev/null"
121
- # if (capture "docker", "inspect",
122
- # "--format='{{.State.Running}}'",
123
- # fetch(:postgres_container_name)).strip == "true"
124
- # else
125
- # execute "docker", "start", fetch(:postgres_container_name)
126
- # end
127
- # else
128
- # set :run_pg_setup, ask("'true' or 'false', - #{fetch(:postgres_container_name)} is not running, would you like to run 'rake pg:setup'?", "false")
129
- # if fetch(:run_pg_setup) == "true"
130
- # #load Gem.bin_path('bundler', 'bundle')
131
- # #import 'postgresinator'
132
- # #load './infrastructure/Rakefile'
133
- # Rake::Task['pg:setup'].invoke
134
- # else
135
- # raise "#{fetch(:postgres_container_name)} is not running, can't run db:migrate!"
136
- # end
137
- # end
138
- # end
139
- # end
140
- before 'deploy:started', :ensure_running_postgres
141
-
142
- before 'deploy:check', :setup_deployer_user do
143
- on "#{ENV['USER']}@#{fetch(:domain)}" do
144
- as :root do
145
- unless test "id", fetch(:deploy_username)
146
- execute "adduser", "--disabled-password", "--gecos", "\"\"", fetch(:deploy_username)
147
- execute "usermod", "-a", "-G", "sudo", fetch(:deploy_username)
148
- execute "usermod", "-a", "-G", "docker", fetch(:deploy_username)
149
- end
150
- execute "mkdir", "-p", "/home/#{fetch(:deploy_username)}/.ssh"
151
- # not actually using ERB interpolation, no need for an instance variable.
152
- template_path = File.expand_path("./templates/deploy/#{fetch(:deploy_username)}_authorized_keys.erb")
153
- generated_config_file = ERB.new(File.new(template_path).read).result(binding)
154
- # upload! does not yet honor "as" and similar scoping methods
155
- upload! StringIO.new(generated_config_file), "/tmp/authorized_keys"
156
- execute "mv", "-b", "/tmp/authorized_keys", "/home/#{fetch(:deploy_username)}/.ssh/authorized_keys"
157
- execute "chown", "-R", "#{fetch(:deploy_username)}:#{fetch(:deploy_username)}", "/home/#{fetch(:deploy_username)}/.ssh"
158
- execute "chmod", "700", "/home/#{fetch(:deploy_username)}/.ssh"
159
- execute "chmod", "600", "/home/#{fetch(:deploy_username)}/.ssh/authorized_keys"
160
- end
161
- end
162
- end
163
-
164
- task :ensure_www_data_user do
165
- on "#{ENV['USER']}@#{fetch(:domain)}" do
166
- as :root do
167
- unless test "id", "www-data"
168
- execute "adduser", "--disabled-password", "--gecos", "\"\"", "www-data"
169
- end
170
- end
171
- end
172
- end
173
- after 'deploy:started', :ensure_www_data_user
174
-
175
- task :set_deployer_user_id do
176
- on roles(:app), in: :sequence, wait: 5 do
177
- set :deployer_user_id, capture("id", "-u", fetch(:deploy_username)).strip
178
- end
179
- end
180
-
181
- task :set_www_data_user_id do
182
- on roles(:app), in: :sequence, wait: 5 do
183
- set :www_data_user_id, capture("id", "-u", "www-data").strip
184
- end
185
- end
186
-
187
- task :chown_log_dir => :set_www_data_user_id do
188
- on roles(:app), in: :sequence, wait: 5 do
189
- as :root do
190
- unless test "[", "-d", "#{fetch(:deploy_to)}/shared/log", "]"
191
- execute("mkdir", "-p", "#{fetch(:deploy_to)}/shared/log")
192
- end
193
- execute "chown", "-R", "#{fetch(:www_data_user_id)}:#{fetch(:www_data_user_id)}", "#{fetch(:deploy_to)}/shared/log"
194
- end
195
- end
196
- end
197
- before 'deploy:check:linked_dirs', :chown_log_dir
198
-
199
- task :setup_deploy_to_dir => :set_deployer_user_id do
83
+ task :install_bundler => ['deployinator:deployment_user'] do
200
84
  on roles(:app), in: :sequence, wait: 5 do
201
- as :root do
202
- [
203
- fetch(:deploy_to),
204
- "#{fetch(:deploy_to)}/shared",
205
- "#{fetch(:deploy_to)}/releases"
206
- ].each do |dir|
207
- unless test "[", "-d", dir, "]"
208
- execute("mkdir", "-p", dir)
209
- end
210
- execute "chown", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", dir
211
- end
212
- end
213
- end
214
- end
215
- after :setup_deployer_user, :setup_deploy_to_dir
216
-
217
- task :install_bundler => :set_deployer_user_id do
218
- on roles(:app), in: :sequence, wait: 5 do
219
- as :root do
220
- unless test "[", "-f", "#{fetch(:deploy_to)}/shared/bundle/bin/bundle", "]"
221
- execute(
222
- "docker", "run", "--rm", "--tty",
223
- "-e", "GEM_HOME=#{fetch(:deploy_to)}/shared/bundle",
224
- "-e", "GEM_PATH=#{fetch(:deploy_to)}/shared/bundle",
225
- "-e", "PATH=#{fetch(:deploy_to)}/shared/bundle/bin:$PATH",
226
- "--entrypoint", "/usr/local/bin/gem",
227
- "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
228
- fetch(:ruby_image_name), "install",
229
- "--install-dir", "#{fetch(:deploy_to)}/shared/bundle",
230
- "--bindir", "#{fetch(:deploy_to)}/shared/bundle/bin",
231
- "--no-ri", "--no-rdoc", "--quiet", "bundler", "-v'#{fetch(:bundler_version)}'"
232
- )
233
- end
234
- execute "chown", "-R", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", "#{fetch(:deploy_to)}/shared/bundle"
85
+ unless file_exists?(shared_path.join('bundle', 'bin', 'bundle'))
86
+ execute(
87
+ "docker", "run", "--rm", "--tty", "--user", fetch(:deployment_user_id),
88
+ "-e", "GEM_HOME=#{shared_path.join('bundle')}",
89
+ "-e", "GEM_PATH=#{shared_path.join('bundle')}",
90
+ "-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
91
+ "--entrypoint", "/bin/bash",
92
+ "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
93
+ fetch(:ruby_image_name), "-c",
94
+ "\"umask", "0007", "&&" "/usr/local/bin/gem", "install",
95
+ "--install-dir", "#{shared_path.join('bundle')}",
96
+ "--bindir", shared_path.join('bundle', 'bin'),
97
+ "--no-ri", "--no-rdoc", "--quiet", "bundler", "-v'#{fetch(:bundler_version)}'\""
98
+ )
235
99
  end
236
100
  end
237
101
  end
238
102
  before 'bundler:install', 'deploy:install_bundler'
239
103
 
240
104
  desc 'Restart application using bluepill restart inside the docker container.'
241
- task :restart => [:set_www_data_user_id, :install_config_files] do
105
+ task :restart => ['deployinator:webserver_user', :install_config_files] do
242
106
  on roles(:app), in: :sequence, wait: 5 do
243
- as :root do
244
- paths = [
245
- fetch(:external_socket_path),
246
- "#{fetch(:deploy_to)}/shared/tmp",
247
- "#{fetch(:deploy_to)}/shared/log/production.log"
248
- ].join(' ')
249
- execute "chown", "-R", "#{fetch(:www_data_user_id)}:#{fetch(:www_data_user_id)}", paths
250
- end
251
- if test "docker", "inspect", fetch(:ruby_container_name), "&>", "/dev/null"
252
- if (capture "docker", "inspect",
253
- "--format='{{.State.Restarting}}'",
254
- fetch(:ruby_container_name)).strip == "true"
107
+ if container_exists?(fetch(:ruby_container_name))
108
+ if container_is_restarting?(fetch(:ruby_container_name))
255
109
  execute("docker", "stop", fetch(:ruby_container_name))
256
110
  end
257
- if (capture "docker", "inspect",
258
- "--format='{{.State.Running}}'",
259
- fetch(:ruby_container_name)).strip == "true"
111
+ if container_is_running?(fetch(:ruby_container_name))
260
112
  execute(
261
113
  "docker", "exec", "--tty",
262
114
  fetch(:ruby_container_name),
263
- "#{fetch(:deploy_to)}/shared/bundle/bin/bluepill",
115
+ shared_path.join('bundle', 'bin', 'bluepill'),
264
116
  fetch(:application), "restart"
265
117
  )
118
+ else
119
+ execute("docker", "start", fetch(:ruby_container_name))
266
120
  end
267
121
  else
268
122
  as :root do
269
- execute("rm", "-f", "#{fetch(:external_socket_path)}/unicorn.pid")
270
- execute "chown", "-R", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", "#{fetch(:deploy_to)}/shared/bundle"
123
+ execute("rm", "-f", fetch(:webserver_socket_path).join('unicorn.pid'))
271
124
  end
272
125
  execute("docker", "run", fetch(:docker_run_bluepill_command))
273
126
  end
@@ -279,27 +132,28 @@ namespace :deploy do
279
132
  namespace :restart do
280
133
  task :force do
281
134
  on roles(:app), in: :sequence, wait: 5 do
282
- if (capture "docker", "inspect",
283
- "--format='{{.State.Running}}'",
284
- fetch(:ruby_container_name)).strip == "true"
285
- execute(
286
- "docker", "exec", "--tty",
287
- fetch(:ruby_container_name),
288
- "#{fetch(:deploy_to)}/shared/bundle/bin/bluepill",
289
- fetch(:application), "stop"
290
- )
291
- sleep 5
292
- end
293
- execute("docker", "stop", fetch(:ruby_container_name))
294
- execute("docker", "wait", fetch(:ruby_container_name))
295
- begin
296
- execute("docker", "rm", fetch(:ruby_container_name))
297
- rescue
298
- sleep 5
135
+ if container_exists?(fetch(:ruby_container_name))
136
+ if container_is_running?(fetch(:ruby_container_name))
137
+ execute(
138
+ "docker", "exec", "--tty",
139
+ fetch(:ruby_container_name),
140
+ shared_path.join('bundle', 'bin', 'bluepill'),
141
+ fetch(:application), "stop"
142
+ )
143
+ sleep 5
144
+ execute("docker", "stop", fetch(:ruby_container_name))
145
+ execute("docker", "wait", fetch(:ruby_container_name))
146
+ else
147
+ end
299
148
  begin
300
149
  execute("docker", "rm", fetch(:ruby_container_name))
301
150
  rescue
302
- fatal "We were not able to remove the container for some reason. Try running 'cap <stage> deploy:restart:force' again."
151
+ sleep 5
152
+ begin
153
+ execute("docker", "rm", fetch(:ruby_container_name))
154
+ rescue
155
+ fatal "We were not able to remove the container for some reason. Try running 'cap <stage> deploy:restart:force' again."
156
+ end
303
157
  end
304
158
  end
305
159
  Rake::Task['deploy:restart'].invoke
@@ -307,70 +161,36 @@ namespace :deploy do
307
161
  end
308
162
  end
309
163
 
310
- after :publishing, :check_nginx_running do
311
- on primary fetch(:migration_role) do
312
- # TODO fix false negative when nginx_container_name is unset
313
- if test "docker", "inspect", fetch(:nginx_container_name), "&>", "/dev/null"
314
- if (capture "docker", "inspect",
315
- "--format='{{.State.Running}}'",
316
- fetch(:nginx_container_name)).strip == "true"
317
- else
318
- 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.)"
319
- end
320
- else
321
- warn "No nginx container named #{fetch(:nginx_container_name)} exists! (You need this, set it up with something like nginxinator.)"
322
- end
323
- end
324
- end
164
+ # after :restart, :clear_cache do
165
+ # on roles(:web), in: :groups, limit: 3, wait: 10 do
166
+ # # Here we can do anything such as:
167
+ # # within release_path do
168
+ # # execute :rake, 'cache:clear'
169
+ # # end
170
+ # end
171
+ # end
325
172
 
326
- after :publishing, :ensure_cadvisor do
173
+ task :install_config_files => ['deployinator:deployment_user', 'deployinator:webserver_user'] do
327
174
  on roles(:app), in: :sequence, wait: 5 do
328
- if fetch(:use_cadvisor, true)
329
- if test "docker", "inspect", "cadvisor", "&>", "/dev/null"
330
- if (capture "docker", "inspect",
331
- "--format='{{.State.Running}}'",
332
- "cadvisor").strip == "true"
333
- info "cadvisor is already running."
334
- else
335
- info "Restarting existing cadvisor container."
336
- execute "docker", "start", "cadvisor"
337
- end
338
- else
339
- execute("docker", "run", fetch(:docker_run_cadvisor_command))
175
+ set :bluepill_config, -> { "bluepill.rb" }
176
+ set :unicorn_config, -> { "unicorn.rb" }
177
+ set :socket_path, -> { fetch(:webserver_socket_path) }
178
+ [fetch(:bluepill_config), fetch(:unicorn_config)].each do |config_file|
179
+ template_path = File.expand_path("./templates/deploy/#{config_file}.erb")
180
+ generated_config_file = ERB.new(File.new(template_path).read).result(binding)
181
+ set :final_path, -> { release_path.join('config', config_file) }
182
+ upload! StringIO.new(generated_config_file), "/tmp/#{config_file}"
183
+ execute("mv", "/tmp/#{config_file}", fetch(:final_path))
184
+ as :root do
185
+ execute("chown", "#{fetch(:deployment_user_id)}:#{fetch(:webserver_user_id)}", fetch(:final_path))
340
186
  end
341
187
  end
342
188
  end
343
189
  end
344
190
 
345
- after :restart, :clear_cache do
346
- on roles(:web), in: :groups, limit: 3, wait: 10 do
347
- # Here we can do anything such as:
348
- # within release_path do
349
- # execute :rake, 'cache:clear'
350
- # end
351
- end
352
- end
353
-
354
- task :install_config_files => [:set_deployer_user_id] do
355
- on roles(:app), in: :sequence, wait: 5 do
356
- set :bluepill_config, -> { "#{fetch(:application)}_bluepill.rb" }
357
- set :unicorn_config, -> { "#{fetch(:application)}_unicorn.rb" }
358
- set :socket_path, -> { fetch(:internal_socket_path) }
359
- as 'root' do
360
- [fetch(:bluepill_config), fetch(:unicorn_config)].each do |config_file|
361
- @deploy_to = fetch(:deploy_to) # needed for ERB
362
- @internal_socket_path = fetch(:socket_path) # needed for ERB
363
- @application = fetch(:application) # needed for ERB
364
- template_path = File.expand_path("./templates/deploy/#{config_file}.erb")
365
- current_path = Pathname.new("#{fetch(:deploy_to)}/current")
366
- generated_config_file = ERB.new(File.new(template_path).read).result(binding)
367
- set :final_path, -> { fetch(:release_path, current_path).join('config', config_file) }
368
- upload! StringIO.new(generated_config_file), "/tmp/#{config_file}"
369
- execute("mv", "/tmp/#{config_file}", fetch(:final_path))
370
- execute("chown", "#{fetch(:deployer_user_id)}:#{fetch(:deployer_user_id)}", fetch(:final_path))
371
- execute("chmod", "664", fetch(:final_path))
372
- end
373
- end
191
+ after 'deploy:finished', :success_message do
192
+ run_locally do
193
+ info "That was a successful deploy!"
374
194
  end
375
195
  end
376
196
 
@@ -0,0 +1,23 @@
1
+ def red_unicorn_exec
2
+ "<%= shared_path.join('bundle', 'bin', 'red_unicorn') %> --env production --pid-file <%= fetch(:webserver_socket_path) %>/unicorn.pid --unicorn-exec <%= shared_path.join('bundle', 'bin', 'unicorn_rails') %> --unicorn-config <%= current_path.join('config', 'unicorn.rb') %>"
3
+ end
4
+
5
+ Bluepill.application("<%= fetch(:application) %>", :log_file => "<%= shared_path.join('log', 'bluepill.log') %>", :foreground => true) do |app|
6
+ app.process("unicorn") do |process|
7
+ process.pid_file = "<%= fetch(:webserver_socket_path) %>/unicorn.pid"
8
+ process.working_dir = "<%= current_path %>"
9
+ process.environment = {"BUNDLE_GEMFILE" => "<%= current_path.join('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
+ process.uid = process.gid = '<%= fetch(:webserver_username) %>'
14
+ process.start_grace_time = 30.seconds
15
+ process.stop_grace_time = 30.seconds
16
+ process.restart_grace_time = 60.seconds
17
+ process.monitor_children do |child_process|
18
+ child_process.stop_command = "kill -QUIT {{PID}}"
19
+ child_process.checks :mem_usage, :every => 30.seconds, :below => 300.megabytes, :times => [3,4], :fires => :stop
20
+ child_process.checks :cpu_usage, :every => 30.seconds, :below => 50, :times => [3,4], :fires => :stop
21
+ end
22
+ end
23
+ end
@@ -1,8 +1,16 @@
1
1
  # config valid only for Capistrano 3.1
2
2
  lock '3.2.1'
3
3
 
4
- set :application, 'my_app_name'
5
- set :repo_url, 'git@example.com:me/my_repo.git'
4
+ set :application, 'my_app_name'
5
+ set :repo_url, 'git@example.com:me/my_repo.git'
6
+ set :preexisting_ssh_user, ENV['USER']
7
+ set :deployment_username, "deployer" # user with SSH access and passwordless sudo rights
8
+ set :webserver_username, "www-data" # less trusted web server user with limited write permissions
9
+
10
+ set :webserver_writeable_dirs, [shared_path.join('run'), shared_path.join("tmp"), shared_path.join("log")]
11
+ set :webserver_executable_dirs, [shared_path.join("bundle", "bin")]
12
+ set :ignore_permissions_dirs, [shared_path.join("postgres")]
13
+ set :webserver_socket_path, shared_path.join('run')
6
14
 
7
15
  # Default branch is :master
8
16
  # Always use the master branch in production:
@@ -14,24 +22,14 @@ end
14
22
  # Default deploy_to directory is /var/www/my_app
15
23
  # set :deploy_to, '/var/www/my_app'
16
24
 
17
- # Default value for :scm is :git
18
- # set :scm, :git
19
-
20
- # Default value for :format is :pretty
21
- # set :format, :pretty
22
-
23
25
  # Default value for :log_level is :debug
24
26
  # set :log_level, :debug
25
27
 
26
- # Default value for :pty is false
27
- # set :pty, true
28
-
29
28
  # Default value for :linked_files is []
30
29
  # set :linked_files, %w{config/database.yml}
31
30
 
32
31
  # Default value for linked_dirs is []
33
32
  # set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
34
- set :linked_dirs, %w{log tmp}
35
33
 
36
34
  # Default value for default_env is {}
37
35
  # set :default_env, { path: "/opt/ruby/bin:$PATH" }
@@ -39,28 +37,62 @@ set :linked_dirs, %w{log tmp}
39
37
  # Default value for keep_releases is 5
40
38
  # set :keep_releases, 5
41
39
 
42
- namespace :deploy do
43
40
 
44
- ### No need to do anything here, deployinator defines this task.
45
- desc 'Restart application'
46
- task :restart do
47
- on roles(:app), in: :sequence, wait: 5 do
48
- # Your restart mechanism here, for example:
49
- # execute :touch, release_path.join('tmp/restart.txt')
50
- end
51
- end
52
-
53
- after :publishing, :restart
41
+ #----------------------------------------------------
42
+ ## The values below shouldn't need changed under the majority of circumstances.
54
43
 
55
- after :restart, :clear_cache do
56
- on roles(:web), in: :groups, limit: 3, wait: 10 do
57
- # Here we can do anything such as:
58
- # within release_path do
59
- # execute :rake, 'cache:clear'
60
- # end
44
+ # Use `cap <stage> deploy from_local=true` to deploy your
45
+ # locally changed code instead of the code in the git repo. You can also add --trace.
46
+ if ENV['from_local']
47
+ if fetch(:current_stage) == "production"
48
+ run_locally do
49
+ fatal "You are trying to deploy to production using from_local, this should pretty much never be done."
50
+ end
51
+ ask :yes_no, "Are you positive you want to continue?"
52
+ if fetch(:yes_no).chomp.downcase == "yes"
53
+ set :scm, :copy
54
+ else
55
+ exit
61
56
  end
62
57
  end
63
-
58
+ else
59
+ set :scm, :git
64
60
  end
65
61
 
66
- require './config/deploy_deployinator.rb'
62
+ #set :bundle_roles, :all # this is default
63
+ #set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } # this is default
64
+ #set :bundle_binstubs, -> { shared_path.join('bin') } # this is default
65
+ #set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') } # this will be overwritten by deployinator
66
+ #set :bundle_gemfile, -> { release_path.join('Gemfile') } # this will be overwritten by deployinator
67
+ #set :bundle_path, -> { shared_path.join('bundle') } # this is default
68
+ #set :bundle_without, %w{development test}.join(' ') # this is default
69
+ #set :bundle_flags, '--deployment --quiet' # this is default
70
+ #set :bundle_flags, '--deployment'
71
+ #set :bundle_env_variables, {} # this is default
72
+
73
+ set :docker_run_bluepill_command, -> { [
74
+ "--tty", "--detach",
75
+ "--name", fetch(:ruby_container_name),
76
+ "-e", "GEM_HOME=#{shared_path.join('bundle')}",
77
+ "-e", "GEM_PATH=#{shared_path.join('bundle')}",
78
+ "-e", "BUNDLE_GEMFILE=#{current_path.join('Gemfile')}",
79
+ "-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
80
+ "--link", "#{fetch(:postgres_container_name)}:postgres",
81
+ "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
82
+ "--entrypoint", shared_path.join('bundle', 'bin', 'bluepill'),
83
+ "--restart", "always", "--memory", "#{fetch(:ruby_container_max_mem_mb)}m",
84
+ fetch(:ruby_image_name), "load",
85
+ current_path.join('config', 'bluepill.rb')
86
+ ] }
87
+
88
+ set :docker_run_cadvisor_command, -> { [
89
+ "--detach",
90
+ "--name", "cadvisor",
91
+ "--volume", "/:/rootfs:ro",
92
+ "--volume", "/var/run:/var/run:rw",
93
+ "--volume", "/sys:/sys:ro",
94
+ "--volume", "/var/lib/docker/:/var/lib/docker:ro",
95
+ "--publish", "127.0.0.1:8080:8080",
96
+ "--restart", "always",
97
+ "google/cadvisor:latest"
98
+ ] }
@@ -1,50 +1,21 @@
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.
1
+ set :domain, "my-app-staging.example.com"
2
+ set :user_host, "#{fetch(:deployment_username)}@#{fetch(:domain)}"
6
3
 
7
- set :domain, "my-app-staging.example.com"
8
- set :deploy_username, "deployer"
9
- set :user_host, "#{fetch(:deploy_username)}@#{fetch(:domain)}"
4
+ role :app, fetch(:user_host)
5
+ role :web, fetch(:user_host)
6
+ role :db, fetch(:user_host)
10
7
 
11
- role :app, fetch(:user_host)
12
- role :web, fetch(:user_host)
13
- role :db, fetch(:user_host)
8
+ set :rails_env, 'production'
9
+ set :migration_role, 'app' # Defaults to 'db'
10
+ #set :conditionally_migrate, true # Defaults to false. If true, it's skip migration if files in db/migrate not modified
11
+ set :assets_roles, [:app] # Defaults to [:web]
12
+ #set :assets_prefix, 'prepackaged-assets' # Defaults to 'assets' this should match config.assets.prefix in your rails config/application.rb
14
13
 
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'
14
+ set :webserver_container_name, "my-app-staging.example.com-nginx-80-443"
15
+ set :postgres_container_name, "my-app-staging.example.com-postgres-5432-master"
16
+ set :postgres_port, "5432"
17
+ set :ruby_image_name, "snarlysodboxer/ruby:1.9.3-p547"
18
+ set :ruby_container_name, "#{fetch(:domain)}-ruby-bluepill"
19
+ set :ruby_container_max_mem_mb, "1024"
20
+ set :bundler_version, "1.7.4"
21
+ set :use_cadvisor, true
@@ -1,12 +1,12 @@
1
- current_path = "<%= @deploy_to %>/current"
2
- shared_path = "<%= @deploy_to %>/shared"
3
- shared_bundler_gems_path = "<%= @deploy_to %>/shared/bundle"
1
+ current_path = "<%= current_path %>"
2
+ shared_path = "<%= shared_path %>"
3
+ shared_bundler_gems_path = "<%= shared_path.join('bundle') %>"
4
4
 
5
5
  worker_processes 4
6
6
  working_directory current_path
7
- listen "<%= @internal_socket_path %>/unicorn.socket", :backlog => 64
7
+ listen "<%= fetch(:webserver_socket_path) %>/unicorn.socket", :backlog => 64
8
8
  timeout 300
9
- pid "<%= @internal_socket_path %>/unicorn.pid"
9
+ pid "<%= fetch(:webserver_socket_path) %>/unicorn.pid"
10
10
 
11
11
  stderr_path "#{shared_path}/log/unicorn.stderr.log"
12
12
  stdout_path "#{shared_path}/log/unicorn.stdout.log"
@@ -21,7 +21,7 @@ before_fork do |server, worker|
21
21
  end
22
22
 
23
23
  after_fork do |server, worker|
24
- worker.user('www-data', 'www-data') if Process.euid == 0
24
+ worker.user('<%= fetch(:webserver_username) %>', '<%= fetch(:webserver_username) %>') if Process.euid == 0
25
25
 
26
26
  defined?(ActiveRecord::Base) and
27
27
  ActiveRecord::Base.establish_connection
@@ -0,0 +1,194 @@
1
+ namespace :deployinator do
2
+
3
+ # These are the only two tasks using :preexisting_ssh_user
4
+ namespace :deployment_user do
5
+ #desc "Setup or re-setup the deployment user, idempotently"
6
+ task :setup do
7
+ on "#{fetch(:preexisting_ssh_user)}@#{fetch(:domain)}" do
8
+ as :root do
9
+ unix_user_add(fetch(:deployment_username)) unless unix_user_exists?(fetch(:deployment_username))
10
+ execute "usermod", "-a", "-G", "sudo,docker,#{fetch(:webserver_username)}", fetch(:deployment_username)
11
+ execute "mkdir", "-p", "/home/#{fetch(:deployment_username)}/.ssh"
12
+ template_path = File.expand_path("./templates/deploy/deployment_authorized_keys.erb")
13
+ generated_config_file = ERB.new(File.new(template_path).read).result(binding)
14
+ # upload! does not yet honor "as" and similar scoping methods
15
+ upload! StringIO.new(generated_config_file), "/tmp/authorized_keys"
16
+ execute "mv", "-b", "/tmp/authorized_keys", "/home/#{fetch(:deployment_username)}/.ssh/authorized_keys"
17
+ execute "chown", "-R", "#{fetch(:deployment_username)}:#{fetch(:deployment_username)}", "/home/#{fetch(:deployment_username)}/.ssh"
18
+ execute "chmod", "700", "/home/#{fetch(:deployment_username)}/.ssh"
19
+ execute "chmod", "600", "/home/#{fetch(:deployment_username)}/.ssh/authorized_keys"
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ task :deployment_user do
26
+ on "#{fetch(:preexisting_ssh_user)}@#{fetch(:domain)}" do
27
+ as :root do
28
+ if unix_user_exists?(fetch(:deployment_username))
29
+ info "User #{fetch(:deployment_username)} already exists. You can safely re-setup the user with 'deployinator:deployment_user:setup'."
30
+ else
31
+ Rake::Task['deployinator:deployment_user:setup'].invoke
32
+ end
33
+ set :deployment_user_id, unix_user_get_id(fetch(:deployment_username))
34
+ end
35
+ end
36
+ end
37
+
38
+ task :webserver_user do
39
+ on roles(:app) do
40
+ as :root do
41
+ unix_user_add(fetch(:webserver_username)) unless unix_user_exists?(fetch(:webserver_username))
42
+ set :webserver_user_id, unix_user_get_id(fetch(:webserver_username))
43
+ end
44
+ end
45
+ end
46
+
47
+ task :sshkit_umask do
48
+ SSHKit.config.umask = "0027"
49
+ end
50
+ if Rake::Task.task_defined?('deploy:started')
51
+ before 'deploy:started', 'deployinator:sshkit_umask'
52
+ end
53
+
54
+ task :file_permissions => [:deployment_user, :webserver_user] do
55
+ on roles(:app) do
56
+ as :root do
57
+ ignore_options = fetch(:ignore_permissions_dirs).collect do |dir|
58
+ ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
59
+ end
60
+
61
+ # chown
62
+ execute "find", fetch(:deploy_to), ignore_options,
63
+ "-exec", "chown", "#{fetch(:deployment_user_id)}:#{fetch(:webserver_user_id)}", "{}", "+"
64
+
65
+ # chmod executable
66
+ fetch(:webserver_executable_dirs).each do |dir|
67
+ if directory_exists?(dir)
68
+ execute "find", dir, "-type", "f",
69
+ "-exec", "chmod", "0750", "{}", "+"
70
+ end
71
+ ignore_options += ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
72
+ end
73
+
74
+ # chmod writable
75
+ fetch(:webserver_writeable_dirs).each do |dir|
76
+ if directory_exists?(dir)
77
+ execute "find", "-L", dir, "-type", "d",
78
+ "-exec", "chmod", "2770", "{}", "+"
79
+ execute "find", "-L", dir, "-type", "f",
80
+ "-exec", "chmod", "0660", "{}", "+"
81
+ end
82
+ ignore_options += ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
83
+ end
84
+
85
+ # chmod
86
+ execute "find", fetch(:deploy_to), "-type", "d", ignore_options,
87
+ "-exec", "chmod", "2750", "{}", "+"
88
+ execute "find", fetch(:deploy_to), "-type", "f", ignore_options,
89
+ "-exec", "chmod", "0640", "{}", "+"
90
+ end
91
+ end
92
+ end
93
+ after 'deploy:check', 'deployinator:file_permissions'
94
+ before 'deploy:restart', 'deployinator:file_permissions'
95
+
96
+
97
+ task :settings, [:absolute_path, :relative_path] do |t, args|
98
+ run_locally do
99
+ need_moar_settings = false
100
+ settings = File.read(args.absolute_path).split("\nset").collect do |line|
101
+ "set#{line}" if line =~ /^ :/
102
+ end.compact
103
+ if fetch(:print_all, false)
104
+ lines = "\nThe following settings are needed in your config (#{args.relative_path}).\n"
105
+ else
106
+ lines = "\nAdd the following setting(s) to your config (#{args.relative_path}) and try again:\n"
107
+ end
108
+ settings.each do |setting|
109
+ if fetch(setting.split(',')[0].split(':')[1].to_sym).nil? or fetch(:print_all, false)
110
+ lines += setting.chomp == setting ? "#{setting}\n" : setting
111
+ need_moar_settings = true
112
+ end
113
+ end
114
+ if need_moar_settings
115
+ if fetch(:print_all, false)
116
+ info lines
117
+ else
118
+ fatal lines if lines.lines.count > 2
119
+ exit
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ end
126
+
127
+ def container_exists?(container_name)
128
+ test "bash", "-c", "\"docker", "inspect", container_name, "&>", "/dev/null\""
129
+ end
130
+
131
+ def container_is_running?(container_name)
132
+ test "[", "\"`docker", "inspect", "--format='{{.State.Running}}'",
133
+ "#{container_name} 2>&1`\"", "=", "\"true\"", "]"
134
+ end
135
+
136
+ def container_is_restarting?(container_name)
137
+ test "[", "\"`docker", "inspect", "--format='{{.State.Restarting}}'",
138
+ container_name, "2>&1`\"", "=", "\"true\"", "]"
139
+ end
140
+
141
+ def localhost_port_responding?(port)
142
+ test "nc", "127.0.0.1", port, "<", "/dev/null", ">", "/dev/null;",
143
+ "[", "`echo", "$?`", "-eq", "0", "]"
144
+ end
145
+
146
+ def unix_user_exists?(user)
147
+ test "bash", "-c", "\"id", user, "&>", "/dev/null\""
148
+ end
149
+
150
+ def unix_user_add(user)
151
+ execute "adduser", "--disabled-password", "--gecos", "\"\"", user
152
+ end
153
+
154
+ def unix_user_get_id(user)
155
+ capture("id", "-u", user).strip
156
+ end
157
+
158
+ def file_exists?(file)
159
+ test "[", "-f", file, "]"
160
+ end
161
+
162
+ def directory_exists?(dir)
163
+ test "[", "-d", dir, "]"
164
+ end
165
+
166
+ def check_stayed_running(name)
167
+ sleep 3
168
+ unless container_is_running?(name)
169
+ fatal "Container #{name} on #{fetch(:domain)} did not stay running more than 3 seconds"
170
+ exit
171
+ end
172
+ if container_is_restarting?(name)
173
+ fatal "Container #{name} on #{fetch(:domain)} is stuck restarting itself."
174
+ exit
175
+ end
176
+ end
177
+
178
+ def create_container(name, command)
179
+ warn "Starting a new container named #{name} on #{fetch(:domain)}"
180
+ execute("docker", "run", command)
181
+ check_stayed_running(name)
182
+ end
183
+
184
+ def start_container(name)
185
+ warn "Starting an existing but non-running container named #{name}"
186
+ execute("docker", "start", name)
187
+ check_stayed_running(name)
188
+ end
189
+
190
+ def restart_container(name)
191
+ warn "Restarting a running container named #{name}"
192
+ execute("docker", "restart", name)
193
+ check_stayed_running(name)
194
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deployinator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-12-11 00:00:00.000000000 Z
12
+ date: 2014-12-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: capistrano
@@ -35,16 +35,16 @@ extra_rdoc_files: []
35
35
  files:
36
36
  - lib/deployinator.rb
37
37
  - lib/deployinator/deploy.rb
38
+ - lib/deployinator/check.rb
38
39
  - lib/deployinator/config.rb
40
+ - lib/deployinator/helpers.rb
39
41
  - lib/deployinator/examples/Capfile
40
42
  - lib/deployinator/examples/config/deploy.rb
41
- - lib/deployinator/examples/config/deploy_deployinator.rb
42
43
  - lib/deployinator/examples/config/deploy/staging.rb
43
- - lib/deployinator/examples/config/deploy/staging_deployinator.rb
44
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
45
+ - lib/deployinator/examples/deployment_authorized_keys.erb
46
+ - lib/deployinator/examples/unicorn.rb.erb
47
+ - lib/deployinator/examples/bluepill.rb.erb
48
48
  homepage: https://github.com/snarlysodboxer/deployinator
49
49
  licenses:
50
50
  - GNU
@@ -1,27 +0,0 @@
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
@@ -1,19 +0,0 @@
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
- # if using nginxinator, :nginx_container_name will be set by it, so leave this one commented out:
9
- #set :nginx_container_name, "my-app-staging.example.com-nginx-80-443"
10
- set :external_socket_path, "/my-app-staging.example.com-nginx-80-443-conf/run"
11
- set :internal_socket_path, "/var/run/unicorn"
12
- # if using postgresinator, :postgres_container_name will be set by it, so leave this one commented out:
13
- #set :postgres_container_name, "my-app-staging.example.com-postgres-5432-master"
14
- set :postgres_port, "5432"
15
- set :ruby_image_name, "snarlysodboxer/ruby:1.9.3-p547"
16
- set :ruby_container_name, "my-app-staging.example.com-ruby-bluepill"
17
- set :ruby_container_max_mem_mb, "1024"
18
- set :bundler_version, "1.7.4"
19
- #set :use_cadvisor, true # this is default
@@ -1,50 +0,0 @@
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
- ## The values in this file are not meant to be changed and shouldn't
10
- ## need to be under the majority of circumstances:
11
-
12
- #set :bundle_roles, :all # this is default
13
- #set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } # this is default
14
- #set :bundle_binstubs, -> { shared_path.join('bin') } # this is default
15
- set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') } # this is required for deployinator
16
- set :bundle_gemfile, -> { release_path.join('Gemfile') } # this is required for deployinator
17
- #set :bundle_path, -> { shared_path.join('bundle') } # this is default
18
- #set :bundle_without, %w{development test}.join(' ') # this is default
19
- set :bundle_without, %w{development test deployment}.join(' ')
20
- #set :bundle_flags, '--deployment --quiet' # this is default
21
- #set :bundle_flags, '--deployment'
22
- #set :bundle_env_variables, {} # this is default
23
-
24
- set :docker_run_bluepill_command, -> { [
25
- "--tty", "--detach",
26
- "--name", fetch(:ruby_container_name),
27
- "-e", "GEM_HOME=#{fetch(:deploy_to)}/shared/bundle",
28
- "-e", "GEM_PATH=#{fetch(:deploy_to)}/shared/bundle",
29
- "-e", "BUNDLE_GEMFILE=#{fetch(:deploy_to)}/current/Gemfile",
30
- "-e", "PATH=#{fetch(:deploy_to)}/shared/bundle/bin:$PATH",
31
- "--link", "#{fetch(:postgres_container_name)}:postgres",
32
- "--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
33
- "--volume", "#{fetch(:external_socket_path)}:#{fetch(:internal_socket_path)}:rw",
34
- "--entrypoint", "#{fetch(:deploy_to)}/shared/bundle/bin/bluepill",
35
- "--restart", "always", "--memory", "#{fetch(:ruby_container_max_mem_mb)}m",
36
- fetch(:ruby_image_name), "load",
37
- "#{fetch(:deploy_to)}/current/config/#{fetch(:application)}_bluepill.rb"
38
- ] }
39
-
40
- set :docker_run_cadvisor_command, -> { [
41
- "--detach",
42
- "--name", "cadvisor",
43
- "--volume", "/:/rootfs:ro",
44
- "--volume", "/var/run:/var/run:rw",
45
- "--volume", "/sys:/sys:ro",
46
- "--volume", "/var/lib/docker/:/var/lib/docker:ro",
47
- "--publish", "127.0.0.1:8080:8080",
48
- "--restart", "always",
49
- "google/cadvisor:latest"
50
- ] }