deployinator 0.0.7 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/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
- ] }