deployinator 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/deployinator.rb +1 -0
- data/lib/deployinator/built-in.rb +186 -0
- data/lib/deployinator/check.rb +26 -55
- data/lib/deployinator/config.rb +94 -9
- data/lib/deployinator/deploy.rb +57 -82
- data/lib/deployinator/examples/config/deploy.rb +15 -93
- data/lib/deployinator/examples/config/deploy/staging.rb +12 -21
- data/lib/deployinator/helpers.rb +101 -145
- metadata +41 -7
data/lib/deployinator.rb
CHANGED
@@ -0,0 +1,186 @@
|
|
1
|
+
set :deploy_log_level, "debug"
|
2
|
+
set :webserver_socket_path, -> { shared_path.join('run') }
|
3
|
+
set :deploy_templates_path, "templates/deploy"
|
4
|
+
|
5
|
+
# Default deploy_to directory is /var/www/my_app
|
6
|
+
# set :deploy_to, '/var/www/my_app'
|
7
|
+
|
8
|
+
# Default value for :log_level is :debug
|
9
|
+
# set :log_level, :debug
|
10
|
+
|
11
|
+
# Default value for :linked_files is []
|
12
|
+
# set :linked_files, %w{config/database.yml}
|
13
|
+
|
14
|
+
# Default value for linked_dirs is []
|
15
|
+
# set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
|
16
|
+
|
17
|
+
# Default value for default_env is {}
|
18
|
+
# set :default_env, { path: "/opt/ruby/bin:$PATH" }
|
19
|
+
|
20
|
+
# Default value for keep_releases is 5
|
21
|
+
# set :keep_releases, 5
|
22
|
+
|
23
|
+
#set :bundle_roles, :all # this is default
|
24
|
+
#set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } # this is default
|
25
|
+
#set :bundle_binstubs, -> { shared_path.join('bin') } # this is default
|
26
|
+
#set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') } # this will be overwritten by deployinator
|
27
|
+
#set :bundle_gemfile, -> { release_path.join('Gemfile') } # this will be overwritten by deployinator
|
28
|
+
#set :bundle_path, -> { shared_path.join('bundle') } # this is default
|
29
|
+
#set :bundle_without, %w{development test}.join(' ') # this is default
|
30
|
+
#set :bundle_flags, '--deployment --quiet' # this is default
|
31
|
+
#set :bundle_flags, '--deployment'
|
32
|
+
#set :bundle_env_variables, {} # this is default
|
33
|
+
set :migration_role, :db # Defaults to 'db'
|
34
|
+
#set :conditionally_migrate, true # Defaults to false. If true, it's skip migration if files in db/migrate not modified
|
35
|
+
set :assets_roles, [:app] # Defaults to [:web]
|
36
|
+
#set :assets_prefix, 'prepackaged-assets' # Defaults to 'assets' this should match config.assets.prefix in your rails config/application.rb
|
37
|
+
|
38
|
+
# TODO: fix from_local, right now you have to copy-paste the set_scm method to your deploy.rb
|
39
|
+
# Use `cap <stage> deploy from_local=true` to deploy your locally changed code
|
40
|
+
# instead of the code in the git repo. You can also add --trace.
|
41
|
+
# You can set include_dir and exclude_dir settings (from capistrano-scm-copy gem).
|
42
|
+
# These will only apply when using the from_local=true option
|
43
|
+
# set :include_dir, '../.*'
|
44
|
+
# set :exclude_dir, ["../.$", "../..", '.././infrastructure']
|
45
|
+
def set_scm
|
46
|
+
if ENV['from_local']
|
47
|
+
if "#{fetch(:stage)}" == "production"
|
48
|
+
run_locally do
|
49
|
+
fatal("You are trying to deploy to production using from_local, " +
|
50
|
+
"this should pretty much never be done.")
|
51
|
+
end
|
52
|
+
ask :yes_no, "Are you positive you want to continue?"
|
53
|
+
case fetch(:yes_no).chomp.downcase
|
54
|
+
when "yes"
|
55
|
+
when "no"
|
56
|
+
exit
|
57
|
+
else
|
58
|
+
warn "Please enter 'yes' or 'no'"
|
59
|
+
set_scm
|
60
|
+
end
|
61
|
+
end
|
62
|
+
set :scm, :copy
|
63
|
+
else
|
64
|
+
set :scm, :git
|
65
|
+
end
|
66
|
+
end
|
67
|
+
set_scm
|
68
|
+
|
69
|
+
def deploy_run_bluepill(host)
|
70
|
+
execute(
|
71
|
+
"docker", "run", "--tty", "--detach",
|
72
|
+
"--name", fetch(:ruby_container_name),
|
73
|
+
"-e", "GEM_HOME=#{shared_path.join('bundle')}",
|
74
|
+
"-e", "GEM_PATH=#{shared_path.join('bundle')}",
|
75
|
+
"-e", "BUNDLE_GEMFILE=#{current_path.join('Gemfile')}",
|
76
|
+
"-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
|
77
|
+
"--restart", "always", "--memory", "#{fetch(:ruby_container_max_mem_mb)}m",
|
78
|
+
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
79
|
+
"--entrypoint", shared_path.join('bundle', 'bin', 'bluepill'),
|
80
|
+
fetch(:ruby_image_name), "load",
|
81
|
+
current_path.join('config', 'bluepill.rb')
|
82
|
+
)
|
83
|
+
end
|
84
|
+
def deploy_run_cadvisor(host)
|
85
|
+
execute(
|
86
|
+
"docker", "run", "--detach",
|
87
|
+
"--name", "cadvisor",
|
88
|
+
"--volume", "/:/rootfs:ro",
|
89
|
+
"--volume", "/var/run:/var/run:rw",
|
90
|
+
"--volume", "/sys:/sys:ro",
|
91
|
+
"--volume", "/var/lib/docker/:/var/lib/docker:ro",
|
92
|
+
"--publish", "127.0.0.1:8080:8080",
|
93
|
+
"--restart", "always",
|
94
|
+
"google/cadvisor:latest"
|
95
|
+
)
|
96
|
+
end
|
97
|
+
def deploy_rails_console(host)
|
98
|
+
[
|
99
|
+
"ssh", "-t", "#{host}", "\"docker", "exec", "--interactive", "--tty",
|
100
|
+
fetch(:ruby_container_name),
|
101
|
+
"bash", "-c", "'cd", current_path, "&&",
|
102
|
+
shared_path.join('bundle', 'bin', 'rails'),
|
103
|
+
"console", "#{fetch(:rails_env)}'\""
|
104
|
+
].join(' ')
|
105
|
+
end
|
106
|
+
def deploy_rails_console_print(host)
|
107
|
+
[
|
108
|
+
"docker", "exec", "--interactive", "--tty",
|
109
|
+
fetch(:ruby_container_name),
|
110
|
+
"bash", "-c", "\"cd", current_path, "&&",
|
111
|
+
shared_path.join('bundle', 'bin', 'rails'),
|
112
|
+
"console", "#{fetch(:rails_env)}\""
|
113
|
+
].join(' ')
|
114
|
+
end
|
115
|
+
def sshkit_bundle_command_map
|
116
|
+
[
|
117
|
+
"/usr/bin/env docker run --rm --tty",
|
118
|
+
"--user", fetch(:deployment_username),
|
119
|
+
"-e", "GEM_HOME=#{shared_path.join('bundle')}",
|
120
|
+
"-e", "GEM_PATH=#{shared_path.join('bundle')}",
|
121
|
+
"-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
|
122
|
+
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
123
|
+
"--volume", "$SSH_AUTH_SOCK:/ssh-agent --env SSH_AUTH_SOCK=/ssh-agent",
|
124
|
+
"--volume", "/home:/home:rw",
|
125
|
+
"--volume", "/etc/passwd:/etc/passwd:ro",
|
126
|
+
"--volume", "/etc/group:/etc/group:ro",
|
127
|
+
"--entrypoint", "#{shared_path.join('bundle', 'bin', 'bundle')}",
|
128
|
+
fetch(:ruby_image_name)
|
129
|
+
].join(' ')
|
130
|
+
end
|
131
|
+
def deploy_assets_precompile(host)
|
132
|
+
execute("docker", "run", "--rm", "--tty", "--user", fetch(:webserver_username),
|
133
|
+
"-w", release_path,
|
134
|
+
"--volume", "/home:/home:rw",
|
135
|
+
"--volume", "/etc/passwd:/etc/passwd:ro",
|
136
|
+
"--volume", "/etc/group:/etc/group:ro",
|
137
|
+
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
138
|
+
"--entrypoint", "/bin/bash",
|
139
|
+
fetch(:ruby_image_name), "-c",
|
140
|
+
"\"umask", "0007", "&&", "#{shared_path.join('bundle', 'bin', 'rake')}",
|
141
|
+
"assets:precompile\"")
|
142
|
+
end
|
143
|
+
def deploy_assets_cleanup(host)
|
144
|
+
execute("docker", "run", "--rm", "--tty",
|
145
|
+
"-e", "RAILS_ENV=#{fetch(:rails_env)}",
|
146
|
+
"-w", release_path,
|
147
|
+
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
148
|
+
"--entrypoint", shared_path.join('bundle', 'bin', 'bundle'),
|
149
|
+
fetch(:ruby_image_name), "exec", "rake", "assets:clean")
|
150
|
+
end
|
151
|
+
def deploy_rake_db_migrate(host)
|
152
|
+
execute("docker", "run", "--rm", "--tty",
|
153
|
+
"-w", release_path,
|
154
|
+
"-e", "RAILS_ENV=#{fetch(:rails_env)}",
|
155
|
+
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
156
|
+
"--entrypoint", shared_path.join('bundle', 'bin', 'rake'),
|
157
|
+
fetch(:ruby_image_name), "db:migrate")
|
158
|
+
end
|
159
|
+
def deploy_install_bundler(host)
|
160
|
+
execute("docker", "run", "--rm", "--tty", "--user", fetch(:deployment_username),
|
161
|
+
"-e", "GEM_HOME=#{shared_path.join('bundle')}",
|
162
|
+
"-e", "GEM_PATH=#{shared_path.join('bundle')}",
|
163
|
+
"-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
|
164
|
+
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
165
|
+
"--volume", "/home:/home:rw",
|
166
|
+
"--volume", "/etc/passwd:/etc/passwd:ro",
|
167
|
+
"--volume", "/etc/group:/etc/group:ro",
|
168
|
+
"--entrypoint", "/bin/bash",
|
169
|
+
fetch(:ruby_image_name), "-c",
|
170
|
+
"\"umask", "0007", "&&" "/usr/local/bin/gem", "install",
|
171
|
+
"--install-dir", "#{shared_path.join('bundle')}",
|
172
|
+
"--bindir", shared_path.join('bundle', 'bin'),
|
173
|
+
"--no-ri", "--no-rdoc", "--quiet", "bundler", "-v'#{fetch(:bundler_version)}'\"")
|
174
|
+
end
|
175
|
+
def deploy_bluepill_restart(host)
|
176
|
+
execute("docker", "exec", "--tty",
|
177
|
+
fetch(:ruby_container_name),
|
178
|
+
shared_path.join('bundle', 'bin', 'bluepill'),
|
179
|
+
fetch(:application), "restart")
|
180
|
+
end
|
181
|
+
def deploy_bluepill_stop(host)
|
182
|
+
execute("docker", "exec", "--tty",
|
183
|
+
fetch(:ruby_container_name),
|
184
|
+
shared_path.join('bundle', 'bin', 'bluepill'),
|
185
|
+
fetch(:application), "stop")
|
186
|
+
end
|
data/lib/deployinator/check.rb
CHANGED
@@ -4,20 +4,7 @@ namespace :deploy do
|
|
4
4
|
task :bundle_command_map => ['deployinator:deployment_user'] do
|
5
5
|
set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') }
|
6
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(' ')
|
7
|
+
SSHKit.config.command_map[:bundle] = sshkit_bundle_command_map
|
21
8
|
end
|
22
9
|
before 'bundler:install', 'deploy:check:bundle_command_map'
|
23
10
|
|
@@ -26,6 +13,14 @@ namespace :deploy do
|
|
26
13
|
SSHKit.config.command_map[:rm] = "/usr/bin/env sudo rm"
|
27
14
|
end
|
28
15
|
|
16
|
+
before 'deploy:check', :brakeman_reminder do
|
17
|
+
run_locally do
|
18
|
+
warn "Remember to run brakeman before deploying!"
|
19
|
+
ask :return_to_continue, nil
|
20
|
+
set :nothing, fetch(:return_to_continue)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
29
24
|
if Rake::Task.task_defined?("deploy:cleanup")
|
30
25
|
# Append dependancy to existing cleanup task
|
31
26
|
task 'deploy:cleanup' => 'deploy:check:rm_command_map'
|
@@ -45,13 +40,13 @@ namespace :deploy do
|
|
45
40
|
# TODO make this better
|
46
41
|
before 'deploy:check', :templates do
|
47
42
|
run_locally do
|
48
|
-
keys_template = File.expand_path("
|
49
|
-
bluepill_template = File.expand_path("
|
50
|
-
unicorn_template = File.expand_path("
|
43
|
+
keys_template = File.expand_path("./#{fetch(:deploy_templates_path)}/deployment_authorized_keys.erb")
|
44
|
+
bluepill_template = File.expand_path("./#{fetch(:deploy_templates_path)}/bluepill.rb.erb")
|
45
|
+
unicorn_template = File.expand_path("./#{fetch(:deploy_templates_path)}/unicorn.rb.erb")
|
51
46
|
{
|
52
|
-
"You need a
|
53
|
-
"You need a
|
54
|
-
"You need a
|
47
|
+
"You need a #{fetch(:deploy_templates_path)}/deployment_authorized_keys.erb file." => keys_template,
|
48
|
+
"You need a #{fetch(:deploy_templates_path)}/bluepill.rb.erb file." => bluepill_template,
|
49
|
+
"You need a #{fetch(:deploy_templates_path)}/unicorn.rb.erb file." => unicorn_template,
|
55
50
|
}.each do |message, file|
|
56
51
|
fatal(message) and raise unless File.exists? file
|
57
52
|
end
|
@@ -64,9 +59,9 @@ namespace :deploy do
|
|
64
59
|
task :root_dir_permissions => ['deployinator:deployment_user', 'deployinator:webserver_user'] do
|
65
60
|
on roles(:app) do
|
66
61
|
as :root do
|
67
|
-
[fetch(:deploy_to), Pathname.new(fetch(:deploy_to)).join("../")].each do |dir|
|
62
|
+
[fetch(:deploy_to), Pathname.new(fetch(:deploy_to)).join("../"), shared_path].each do |dir|
|
68
63
|
if directory_exists?(dir)
|
69
|
-
execute "chown", "#{fetch(:
|
64
|
+
execute "chown", "#{fetch(:deployment_username)}:#{fetch(:webserver_username)}", dir
|
70
65
|
execute "chmod", "2750", dir
|
71
66
|
end
|
72
67
|
end
|
@@ -75,53 +70,29 @@ namespace :deploy do
|
|
75
70
|
end
|
76
71
|
before 'deploy:check:directories', 'deploy:check:root_dir_permissions'
|
77
72
|
|
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
73
|
before 'deploy:check', :postgres_running do
|
95
|
-
on
|
96
|
-
|
97
|
-
|
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!"
|
74
|
+
on roles(:app) do
|
75
|
+
unless localhost_port_responding?(fetch(:postgres_port))
|
76
|
+
fatal "Port #{fetch(:postgres_port)} is not responding, we won't be able to db:migrate!"
|
108
77
|
raise
|
109
78
|
end
|
110
79
|
end
|
111
80
|
end
|
112
81
|
|
113
82
|
before 'deploy:check', :ensure_cadvisor do
|
114
|
-
on roles(:app)
|
83
|
+
on roles(:app) do |host|
|
115
84
|
if fetch(:use_cadvisor, true)
|
116
85
|
if container_exists?("cadvisor")
|
117
86
|
if container_is_running?("cadvisor")
|
118
87
|
info "cadvisor is already running."
|
119
88
|
else
|
120
|
-
info "
|
121
|
-
|
89
|
+
info "Starting existing cadvisor container."
|
90
|
+
start_container("cadvisor")
|
122
91
|
end
|
123
92
|
else
|
124
|
-
|
93
|
+
warn "Starting a new container named 'cadvisor' on #{host}"
|
94
|
+
deploy_run_cadvisor(host)
|
95
|
+
check_stayed_running("cadvisor")
|
125
96
|
end
|
126
97
|
else
|
127
98
|
info "Not using cadvisor."
|
data/lib/deployinator/config.rb
CHANGED
@@ -1,24 +1,109 @@
|
|
1
|
+
module Capistrano
|
2
|
+
module TaskEnhancements
|
3
|
+
alias_method :deploy_original_default_tasks, :default_tasks
|
4
|
+
def default_tasks
|
5
|
+
deploy_original_default_tasks + [
|
6
|
+
"deployinator:write_built_in",
|
7
|
+
"deployinator:write_example_configs",
|
8
|
+
"deployinator:write_example_configs:in_place"
|
9
|
+
]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
1
14
|
namespace :deployinator do
|
2
15
|
|
16
|
+
set :example, "_example"
|
17
|
+
|
3
18
|
desc 'Write example config files'
|
4
19
|
task :write_example_configs do
|
5
20
|
run_locally do
|
6
|
-
execute "mkdir", "-p", "config/deploy",
|
21
|
+
execute "mkdir", "-p", "config/deploy", fetch(:deploy_templates_path, 'templates/deploy')
|
7
22
|
{
|
8
|
-
"examples/Capfile" => "
|
9
|
-
"examples/config/deploy.rb" => "config/
|
10
|
-
"examples/config/deploy/staging.rb" => "config/deploy/
|
11
|
-
"examples/Dockerfile" => "templates/deploy/
|
12
|
-
"examples/deployment_authorized_keys.erb" => "templates/deploy/
|
13
|
-
"examples/unicorn.rb.erb" => "templates/deploy/
|
14
|
-
"examples/bluepill.rb.erb" => "templates/deploy/
|
23
|
+
"examples/Capfile" => "Capfile#{fetch(:example)}",
|
24
|
+
"examples/config/deploy.rb" => "config/deploy#{fetch(:example)}.rb",
|
25
|
+
"examples/config/deploy/staging.rb" => "config/deploy/staging#{fetch(:example)}.rb",
|
26
|
+
"examples/Dockerfile" => "#{fetch(:deploy_templates_path, 'templates/deploy')}/Dockerfile#{fetch(:example)}",
|
27
|
+
"examples/deployment_authorized_keys.erb" => "#{fetch(:deploy_templates_path, 'templates/deploy')}/deployment_authorized_keys#{fetch(:example)}.erb",
|
28
|
+
"examples/unicorn.rb.erb" => "#{fetch(:deploy_templates_path, 'templates/deploy')}/unicorn#{fetch(:example)}.rb.erb",
|
29
|
+
"examples/bluepill.rb.erb" => "#{fetch(:deploy_templates_path, 'templates/deploy')}/bluepill#{fetch(:example)}.rb.erb"
|
15
30
|
}.each do |source, destination|
|
16
31
|
config = File.read(File.dirname(__FILE__) + "/#{source}")
|
17
32
|
File.open("./#{destination}", 'w') { |f| f.write(config) }
|
18
33
|
info "Wrote '#{destination}'"
|
19
34
|
end
|
20
|
-
|
35
|
+
unless fetch(:example).empty?
|
36
|
+
info "Now remove the '#{fetch(:example)}' portion of their names or diff with existing files and add the needed lines."
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
desc 'Write example config files (will overwrite any existing config files).'
|
42
|
+
namespace :write_example_configs do
|
43
|
+
task :in_place do
|
44
|
+
set :example, ""
|
45
|
+
Rake::Task['deployinator:write_example_configs'].invoke
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'Write a file showing the built-in overridable settings.'
|
50
|
+
task :write_built_in do
|
51
|
+
run_locally do
|
52
|
+
{
|
53
|
+
'built-in.rb' => 'built-in.rb',
|
54
|
+
}.each do |source, destination|
|
55
|
+
config = File.read(File.dirname(__FILE__) + "/#{source}")
|
56
|
+
File.open("./#{destination}", 'w') { |f| f.write(config) }
|
57
|
+
info "Wrote '#{destination}'"
|
58
|
+
end
|
59
|
+
info "Now examine the file and copy-paste into your deploy.rb or <stage>.rb and customize."
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# These are the only two tasks using :preexisting_ssh_user
|
64
|
+
namespace :deployment_user do
|
65
|
+
#desc "Setup or re-setup the deployment user, idempotently"
|
66
|
+
task :setup do
|
67
|
+
on roles(:all) do |h|
|
68
|
+
on "#{fetch(:preexisting_ssh_user)}@#{h}" do |host|
|
69
|
+
as :root do
|
70
|
+
deployment_user_setup(fetch(:deploy_templates_path, 'templates/deploy'))
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
task :deployment_user do
|
78
|
+
on roles(:all) do |h|
|
79
|
+
on "#{fetch(:preexisting_ssh_user)}@#{h}" do |host|
|
80
|
+
as :root do
|
81
|
+
if unix_user_exists?(fetch(:deployment_username))
|
82
|
+
info "User #{fetch(:deployment_username)} already exists. You can safely re-setup the user with 'deployinator:deployment_user:setup'."
|
83
|
+
else
|
84
|
+
Rake::Task['deployinator:deployment_user:setup'].invoke
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
task :webserver_user do
|
92
|
+
on roles(:app) do
|
93
|
+
as :root do
|
94
|
+
unix_user_add(fetch(:webserver_username)) unless unix_user_exists?(fetch(:webserver_username))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
task :file_permissions => [:deployment_user, :webserver_user] do
|
100
|
+
on roles(:app) do
|
101
|
+
as :root do
|
102
|
+
setup_file_permissions
|
103
|
+
end
|
21
104
|
end
|
22
105
|
end
|
106
|
+
after 'deploy:check', 'deployinator:file_permissions'
|
107
|
+
before 'deploy:restart', 'deployinator:file_permissions'
|
23
108
|
|
24
109
|
end
|
data/lib/deployinator/deploy.rb
CHANGED
@@ -3,6 +3,22 @@ lock '3.2.1'
|
|
3
3
|
|
4
4
|
namespace :deploy do
|
5
5
|
|
6
|
+
before :starting, :log_level do
|
7
|
+
SSHKit.config.output_verbosity = fetch(:deploy_log_level)
|
8
|
+
end
|
9
|
+
|
10
|
+
before :starting, 'deployinator:sshkit_umask'
|
11
|
+
|
12
|
+
# Default branch is :master
|
13
|
+
before :starting, :set_branch do
|
14
|
+
unless ENV['from_local']
|
15
|
+
# Always use the master branch in production:
|
16
|
+
unless "#{fetch(:stage)}" == "production"
|
17
|
+
ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
6
22
|
#desc 'Copies .git folder to support .gemspecs that run git commands'
|
7
23
|
task :copy_git do
|
8
24
|
unless ENV['from_local'] == "true"
|
@@ -17,22 +33,11 @@ namespace :deploy do
|
|
17
33
|
|
18
34
|
if Rake::Task.task_defined?("deploy:assets:precompile")
|
19
35
|
# Overwrite :assets:precompile to use docker
|
20
|
-
Rake::Task["deploy:assets:precompile"].
|
36
|
+
Rake::Task["deploy:assets:precompile"].clear_actions
|
21
37
|
namespace :assets do
|
22
38
|
task :precompile => ['deployinator:deployment_user'] do
|
23
|
-
on roles(fetch(:assets_roles)) do
|
24
|
-
|
25
|
-
"docker", "run", "--rm", "--tty", "--user", fetch(:webserver_username),
|
26
|
-
"-w", release_path,
|
27
|
-
"--link", "#{fetch(:postgres_container_name)}:postgres",
|
28
|
-
"--entrypoint", "/bin/bash",
|
29
|
-
"--volume", "/etc/passwd:/etc/passwd:ro",
|
30
|
-
"--volume", "/etc/group:/etc/group:ro",
|
31
|
-
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
32
|
-
fetch(:ruby_image_name), "-c",
|
33
|
-
"\"umask", "0007", "&&", "#{shared_path.join('bundle', 'bin', 'rake')}",
|
34
|
-
"assets:precompile\""
|
35
|
-
)
|
39
|
+
on roles(fetch(:assets_roles)) do |host|
|
40
|
+
deploy_assets_precompile(host)
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
@@ -40,64 +45,37 @@ namespace :deploy do
|
|
40
45
|
|
41
46
|
if Rake::Task.task_defined?("deploy:cleanup_assets")
|
42
47
|
# Overwrite :cleanup_assets to use docker
|
43
|
-
Rake::Task["deploy:cleanup_assets"].
|
48
|
+
Rake::Task["deploy:cleanup_assets"].clear_actions
|
44
49
|
desc 'Cleanup expired assets'
|
45
50
|
task :cleanup_assets => [:set_rails_env] do
|
46
|
-
on roles(fetch(:assets_roles)) do
|
47
|
-
|
48
|
-
"docker", "run", "--rm", "--tty",
|
49
|
-
"-e", "RAILS_ENV=#{fetch(:rails_env)}",
|
50
|
-
"-w", release_path,
|
51
|
-
"--entrypoint", shared_path.join('bundle', 'bin', 'bundle'),
|
52
|
-
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
53
|
-
fetch(:ruby_image_name), "exec", "rake", "assets:clean"
|
54
|
-
)
|
51
|
+
on roles(fetch(:assets_roles)) do |host|
|
52
|
+
deploy_assets_cleanup(host)
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|
58
56
|
|
59
57
|
if Rake::Task.task_defined?("deploy:migrate")
|
60
58
|
# Overwrite :migrate to use docker
|
61
|
-
Rake::Task["deploy:migrate"].
|
59
|
+
Rake::Task["deploy:migrate"].clear_actions
|
62
60
|
desc 'Runs rake db:migrate if migrations are set'
|
63
61
|
task :migrate => [:set_rails_env, 'deploy:check:postgres_running'] do
|
64
|
-
on primary fetch(:migration_role) do
|
62
|
+
on primary fetch(:migration_role) do |host|
|
65
63
|
conditionally_migrate = fetch(:conditionally_migrate)
|
66
64
|
info '[deploy:migrate] Checking changes in /db/migrate' if conditionally_migrate
|
67
65
|
if conditionally_migrate && test("diff -q #{release_path}/db/migrate #{current_path}/db/migrate")
|
68
66
|
info '[deploy:migrate] Skip `deploy:migrate` (nothing changed in db/migrate)'
|
69
67
|
else
|
70
68
|
info '[deploy:migrate] Run `rake db:migrate`' if conditionally_migrate
|
71
|
-
|
72
|
-
"docker", "run", "--rm", "--tty",
|
73
|
-
"--link", "#{fetch(:postgres_container_name)}:postgres",
|
74
|
-
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
75
|
-
"-e", "RAILS_ENV=#{fetch(:rails_env)}",
|
76
|
-
"--entrypoint", shared_path.join('bundle', 'bin', 'rake'),
|
77
|
-
"-w", release_path,
|
78
|
-
fetch(:ruby_image_name), "db:migrate"
|
79
|
-
)
|
69
|
+
deploy_rake_db_migrate(host)
|
80
70
|
end
|
81
71
|
end
|
82
72
|
end
|
83
73
|
end
|
84
74
|
|
85
75
|
task :install_bundler => ['deployinator:deployment_user'] do
|
86
|
-
on roles(:app)
|
76
|
+
on roles(:app) do |host|
|
87
77
|
unless file_exists?(shared_path.join('bundle', 'bin', 'bundle'))
|
88
|
-
|
89
|
-
"docker", "run", "--rm", "--tty", "--user", fetch(:deployment_user_id),
|
90
|
-
"-e", "GEM_HOME=#{shared_path.join('bundle')}",
|
91
|
-
"-e", "GEM_PATH=#{shared_path.join('bundle')}",
|
92
|
-
"-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
|
93
|
-
"--entrypoint", "/bin/bash",
|
94
|
-
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
95
|
-
fetch(:ruby_image_name), "-c",
|
96
|
-
"\"umask", "0007", "&&" "/usr/local/bin/gem", "install",
|
97
|
-
"--install-dir", "#{shared_path.join('bundle')}",
|
98
|
-
"--bindir", shared_path.join('bundle', 'bin'),
|
99
|
-
"--no-ri", "--no-rdoc", "--quiet", "bundler", "-v'#{fetch(:bundler_version)}'\""
|
100
|
-
)
|
78
|
+
deploy_install_bundler(host)
|
101
79
|
end
|
102
80
|
end
|
103
81
|
end
|
@@ -105,26 +83,24 @@ namespace :deploy do
|
|
105
83
|
|
106
84
|
desc 'Restart application using bluepill restart inside the docker container.'
|
107
85
|
task :restart => ['deployinator:webserver_user', :install_config_files] do
|
108
|
-
on roles(:app)
|
109
|
-
|
110
|
-
|
111
|
-
|
86
|
+
on roles(:app) do |host|
|
87
|
+
name = fetch(:ruby_container_name)
|
88
|
+
if container_exists?(name)
|
89
|
+
if container_is_restarting?(name)
|
90
|
+
execute("docker", "stop", name)
|
112
91
|
end
|
113
|
-
if container_is_running?(
|
114
|
-
|
115
|
-
"docker", "exec", "--tty",
|
116
|
-
fetch(:ruby_container_name),
|
117
|
-
shared_path.join('bundle', 'bin', 'bluepill'),
|
118
|
-
fetch(:application), "restart"
|
119
|
-
)
|
92
|
+
if container_is_running?(name)
|
93
|
+
deploy_bluepill_restart(host)
|
120
94
|
else
|
121
|
-
execute("docker", "start",
|
95
|
+
execute("docker", "start", name)
|
122
96
|
end
|
123
97
|
else
|
124
98
|
as :root do
|
125
99
|
execute("rm", "-f", fetch(:webserver_socket_path).join('unicorn.pid'))
|
126
100
|
end
|
127
|
-
|
101
|
+
warn "Starting a new container named #{name} on #{host}"
|
102
|
+
deploy_run_bluepill(host)
|
103
|
+
check_stayed_running(name)
|
128
104
|
end
|
129
105
|
end
|
130
106
|
end
|
@@ -133,15 +109,10 @@ namespace :deploy do
|
|
133
109
|
desc 'Restart application by recreating the docker container.'
|
134
110
|
namespace :restart do
|
135
111
|
task :force do
|
136
|
-
on roles(:app)
|
112
|
+
on roles(:app) do |host|
|
137
113
|
if container_exists?(fetch(:ruby_container_name))
|
138
114
|
if container_is_running?(fetch(:ruby_container_name))
|
139
|
-
|
140
|
-
"docker", "exec", "--tty",
|
141
|
-
fetch(:ruby_container_name),
|
142
|
-
shared_path.join('bundle', 'bin', 'bluepill'),
|
143
|
-
fetch(:application), "stop"
|
144
|
-
)
|
115
|
+
deploy_bluepill_stop(host)
|
145
116
|
sleep 5
|
146
117
|
execute("docker", "stop", fetch(:ruby_container_name))
|
147
118
|
execute("docker", "wait", fetch(:ruby_container_name))
|
@@ -173,33 +144,37 @@ namespace :deploy do
|
|
173
144
|
# end
|
174
145
|
|
175
146
|
task :install_config_files => ['deployinator:deployment_user', 'deployinator:webserver_user'] do
|
176
|
-
on roles(:app)
|
147
|
+
on roles(:app) do |host|
|
177
148
|
set :bluepill_config, -> { "bluepill.rb" }
|
178
149
|
set :unicorn_config, -> { "unicorn.rb" }
|
179
150
|
set :socket_path, -> { fetch(:webserver_socket_path) }
|
180
151
|
[fetch(:bluepill_config), fetch(:unicorn_config)].each do |config_file|
|
181
|
-
template_path = File.expand_path("
|
152
|
+
template_path = File.expand_path("./#{fetch(:deploy_templates_path)}/#{config_file}.erb")
|
182
153
|
generated_config_file = ERB.new(File.new(template_path).read).result(binding)
|
183
154
|
set :final_path, -> { release_path.join('config', config_file) }
|
184
155
|
upload! StringIO.new(generated_config_file), "/tmp/#{config_file}"
|
185
156
|
execute("mv", "/tmp/#{config_file}", fetch(:final_path))
|
186
157
|
as :root do
|
187
|
-
execute("chown", "#{fetch(:
|
158
|
+
execute("chown", "#{fetch(:deployment_username)}:#{fetch(:webserver_username)}", fetch(:final_path))
|
188
159
|
end
|
189
160
|
end
|
190
161
|
end
|
191
162
|
end
|
192
163
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
164
|
+
desc "Enter the Rails console."
|
165
|
+
task :rails_console do
|
166
|
+
on roles(:app) do |host|
|
167
|
+
info "Entering Rails Console inside #{fetch(:ruby_container_name)} on #{host}"
|
168
|
+
system deploy_rails_console(host)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
namespace :rails_console do
|
173
|
+
task :print do
|
174
|
+
on roles(:app) do |host|
|
175
|
+
info "You can SSH into #{host} and run the following command to enter the Rails Console."
|
176
|
+
info deploy_rails_console_print(host)
|
177
|
+
end
|
203
178
|
end
|
204
179
|
end
|
205
180
|
|
@@ -1,99 +1,21 @@
|
|
1
|
-
# config valid only for Capistrano 3.1
|
1
|
+
# config valid only for Capistrano 3.2.1
|
2
2
|
lock '3.2.1'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
set :
|
7
|
-
set :
|
8
|
-
set :
|
9
|
-
|
10
|
-
set :
|
11
|
-
set :
|
12
|
-
set :
|
13
|
-
set :
|
14
|
-
set :
|
15
|
-
|
16
|
-
# Default branch is :master
|
17
|
-
# Always use the master branch in production:
|
18
|
-
set :current_stage, -> { fetch(:stage).to_s.strip }
|
19
|
-
unless fetch(:current_stage) == "production"
|
20
|
-
ask :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }.call
|
21
|
-
end
|
22
|
-
|
23
|
-
# Default deploy_to directory is /var/www/my_app
|
24
|
-
# set :deploy_to, '/var/www/my_app'
|
25
|
-
|
26
|
-
# Default value for :log_level is :debug
|
27
|
-
# set :log_level, :debug
|
28
|
-
|
29
|
-
# Default value for :linked_files is []
|
30
|
-
# set :linked_files, %w{config/database.yml}
|
4
|
+
##### deployinator
|
5
|
+
### ------------------------------------------------------------------
|
6
|
+
set :repo_url, 'git@example.com:me/my_repo.git'
|
7
|
+
set :application, 'my_app_name'
|
8
|
+
set :preexisting_ssh_user, ENV['USER']
|
9
|
+
set :deployment_username, "deployer" # user with SSH access and passwordless sudo rights
|
10
|
+
set :webserver_username, "www-data" # less trusted web server user with limited write permissions
|
11
|
+
set :webserver_owned_dirs, [shared_path.join('tmp', 'cache'), shared_path.join('public', 'assets')]
|
12
|
+
set :webserver_writeable_dirs, [shared_path.join('run'), shared_path.join("tmp"), shared_path.join("log")]
|
13
|
+
set :webserver_executable_dirs, [shared_path.join("bundle", "bin")]
|
14
|
+
set :ignore_permissions_dirs, [shared_path.join("postgres"), shared_path.join("nginx")]
|
31
15
|
|
32
16
|
# Default value for linked_dirs is []
|
33
17
|
# set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
|
34
18
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# Default value for keep_releases is 5
|
39
|
-
# set :keep_releases, 5
|
40
|
-
|
41
|
-
|
42
|
-
#----------------------------------------------------
|
43
|
-
## The values below shouldn't need changed under the majority of circumstances.
|
44
|
-
|
45
|
-
# Use `cap <stage> deploy from_local=true` to deploy your
|
46
|
-
# locally changed code instead of the code in the git repo. You can also add --trace.
|
47
|
-
if ENV['from_local']
|
48
|
-
if fetch(:current_stage) == "production"
|
49
|
-
run_locally do
|
50
|
-
fatal "You are trying to deploy to production using from_local, this should pretty much never be done."
|
51
|
-
end
|
52
|
-
ask :yes_no, "Are you positive you want to continue?"
|
53
|
-
if fetch(:yes_no).chomp.downcase == "yes"
|
54
|
-
set :scm, :copy
|
55
|
-
else
|
56
|
-
exit
|
57
|
-
end
|
58
|
-
end
|
59
|
-
else
|
60
|
-
set :scm, :git
|
61
|
-
end
|
62
|
-
|
63
|
-
#set :bundle_roles, :all # this is default
|
64
|
-
#set :bundle_servers, -> { release_roles(fetch(:bundle_roles)) } # this is default
|
65
|
-
#set :bundle_binstubs, -> { shared_path.join('bin') } # this is default
|
66
|
-
#set :bundle_binstubs, -> { shared_path.join('bundle', 'bin') } # this will be overwritten by deployinator
|
67
|
-
#set :bundle_gemfile, -> { release_path.join('Gemfile') } # this will be overwritten by deployinator
|
68
|
-
#set :bundle_path, -> { shared_path.join('bundle') } # this is default
|
69
|
-
#set :bundle_without, %w{development test}.join(' ') # this is default
|
70
|
-
#set :bundle_flags, '--deployment --quiet' # this is default
|
71
|
-
#set :bundle_flags, '--deployment'
|
72
|
-
#set :bundle_env_variables, {} # this is default
|
73
|
-
|
74
|
-
set :docker_run_bluepill_command, -> { [
|
75
|
-
"--tty", "--detach",
|
76
|
-
"--name", fetch(:ruby_container_name),
|
77
|
-
"-e", "GEM_HOME=#{shared_path.join('bundle')}",
|
78
|
-
"-e", "GEM_PATH=#{shared_path.join('bundle')}",
|
79
|
-
"-e", "BUNDLE_GEMFILE=#{current_path.join('Gemfile')}",
|
80
|
-
"-e", "PATH=#{shared_path.join('bundle', 'bin')}:$PATH",
|
81
|
-
"--link", "#{fetch(:postgres_container_name)}:postgres",
|
82
|
-
"--volume", "#{fetch(:deploy_to)}:#{fetch(:deploy_to)}:rw",
|
83
|
-
"--entrypoint", shared_path.join('bundle', 'bin', 'bluepill'),
|
84
|
-
"--restart", "always", "--memory", "#{fetch(:ruby_container_max_mem_mb)}m",
|
85
|
-
fetch(:ruby_image_name), "load",
|
86
|
-
current_path.join('config', 'bluepill.rb')
|
87
|
-
] }
|
88
|
-
|
89
|
-
set :docker_run_cadvisor_command, -> { [
|
90
|
-
"--detach",
|
91
|
-
"--name", "cadvisor",
|
92
|
-
"--volume", "/:/rootfs:ro",
|
93
|
-
"--volume", "/var/run:/var/run:rw",
|
94
|
-
"--volume", "/sys:/sys:ro",
|
95
|
-
"--volume", "/var/lib/docker/:/var/lib/docker:ro",
|
96
|
-
"--publish", "127.0.0.1:8080:8080",
|
97
|
-
"--restart", "always",
|
98
|
-
"google/cadvisor:latest"
|
99
|
-
] }
|
19
|
+
set :bundler_version, "1.7.4"
|
20
|
+
set :use_cadvisor, true
|
21
|
+
### ------------------------------------------------------------------
|
@@ -1,21 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
set :
|
9
|
-
set :
|
10
|
-
|
11
|
-
set :
|
12
|
-
|
13
|
-
|
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
|
+
##### deployinator
|
2
|
+
### ------------------------------------------------------------------
|
3
|
+
set :domain, "my-app-staging.example.com"
|
4
|
+
server fetch(:domain),
|
5
|
+
:user => fetch(:deployment_username),
|
6
|
+
:roles => ["app", "web", "db"]
|
7
|
+
set :rails_env, 'production'
|
8
|
+
set :ruby_image_name, "snarlysodboxer/ruby:1.9.3-p547"
|
9
|
+
set :ruby_container_name, "#{fetch(:domain)}-ruby-bluepill"
|
10
|
+
set :ruby_container_max_mem_mb, "1024"
|
11
|
+
set :postgres_port, "5432"
|
12
|
+
### ------------------------------------------------------------------
|
data/lib/deployinator/helpers.rb
CHANGED
@@ -1,141 +1,95 @@
|
|
1
1
|
namespace :deployinator do
|
2
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
3
|
task :sshkit_umask do
|
48
4
|
SSHKit.config.umask = "0027"
|
49
5
|
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_ignore_options = fetch(:webserver_owned_dirs).collect do |dir|
|
62
|
-
["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
63
|
-
end
|
64
|
-
|
65
|
-
# chown webserver owned
|
66
|
-
fetch(:webserver_owned_dirs).each do |dir|
|
67
|
-
if directory_exists?(dir)
|
68
|
-
execute "find", dir, ignore_options,
|
69
|
-
"-exec", "chown", "#{fetch(:webserver_user_id)}:#{fetch(:webserver_user_id)}", "{}", "+"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
# chown
|
74
|
-
execute "find", fetch(:deploy_to), ignore_options, chown_ignore_options,
|
75
|
-
"-exec", "chown", "#{fetch(:deployment_user_id)}:#{fetch(:webserver_user_id)}", "{}", "+"
|
76
|
-
|
77
|
-
# chmod executable
|
78
|
-
fetch(:webserver_executable_dirs).each do |dir|
|
79
|
-
if directory_exists?(dir)
|
80
|
-
execute "find", dir, "-type", "f",
|
81
|
-
"-exec", "chmod", "0750", "{}", "+"
|
82
|
-
end
|
83
|
-
ignore_options += ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
84
|
-
end
|
85
|
-
|
86
|
-
# chmod writable
|
87
|
-
fetch(:webserver_writeable_dirs).each do |dir|
|
88
|
-
if directory_exists?(dir)
|
89
|
-
execute "find", "-L", dir, "-type", "d",
|
90
|
-
"-exec", "chmod", "2770", "{}", "+"
|
91
|
-
execute "find", "-L", dir, "-type", "f",
|
92
|
-
"-exec", "chmod", "0660", "{}", "+"
|
93
|
-
end
|
94
|
-
ignore_options += ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
95
|
-
end
|
96
|
-
|
97
|
-
# chmod
|
98
|
-
execute "find", fetch(:deploy_to), "-type", "d", ignore_options,
|
99
|
-
"-exec", "chmod", "2750", "{}", "+"
|
100
|
-
execute "find", fetch(:deploy_to), "-type", "f", ignore_options,
|
101
|
-
"-exec", "chmod", "0640", "{}", "+"
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
after 'deploy:check', 'deployinator:file_permissions'
|
106
|
-
before 'deploy:restart', 'deployinator:file_permissions'
|
107
|
-
|
108
6
|
|
109
7
|
task :settings, [:absolute_path, :relative_path] do |t, args|
|
110
8
|
run_locally do
|
9
|
+
if fetch(:print_all)
|
10
|
+
lines = "\nThe following settings are needed in your config (#{args.relative_path}).\n"
|
11
|
+
lines += File.read(args.absolute_path)
|
12
|
+
info lines
|
13
|
+
break
|
14
|
+
end
|
15
|
+
|
111
16
|
need_moar_settings = false
|
112
17
|
settings = File.read(args.absolute_path).split("\nset").collect do |line|
|
113
18
|
"set#{line}" if line =~ /^ :/
|
114
19
|
end.compact
|
115
|
-
|
116
|
-
lines = "\nThe following settings are needed in your config (#{args.relative_path}).\n"
|
117
|
-
else
|
118
|
-
lines = "\nAdd the following setting(s) to your config (#{args.relative_path}) and try again:\n"
|
119
|
-
end
|
20
|
+
lines = "\nAdd the following setting(s) to your config (#{args.relative_path}) and try again:\n"
|
120
21
|
settings.each do |setting|
|
121
|
-
if fetch(setting.split(',')[0].split(':')[1].to_sym).nil?
|
22
|
+
if fetch(setting.split(',')[0].split(':')[1].to_sym).nil?
|
122
23
|
lines += setting.chomp == setting ? "#{setting}\n" : setting
|
123
24
|
need_moar_settings = true
|
124
25
|
end
|
125
26
|
end
|
126
|
-
if need_moar_settings
|
127
|
-
|
128
|
-
info lines
|
129
|
-
else
|
130
|
-
fatal lines if lines.lines.count > 2
|
131
|
-
exit
|
132
|
-
end
|
133
|
-
end
|
27
|
+
fatal(lines) if(lines.lines.count > 2) if(need_moar_settings)
|
28
|
+
exit if need_moar_settings
|
134
29
|
end
|
135
30
|
end
|
136
31
|
|
137
32
|
end
|
138
33
|
|
34
|
+
|
35
|
+
def deployment_user_setup(templates_path)
|
36
|
+
require 'erb' unless defined?(ERB)
|
37
|
+
name = fetch(:deployment_username)
|
38
|
+
unix_user_add(name) unless unix_user_exists?(name)
|
39
|
+
execute "usermod", "-a", "-G", "sudo,docker,#{fetch(:webserver_username)}", name
|
40
|
+
execute "mkdir", "-p", "/home/#{name}/.ssh"
|
41
|
+
template_path = File.expand_path("./#{templates_path}/deployment_authorized_keys.erb")
|
42
|
+
generated_config_file = ERB.new(File.new(template_path).read).result(binding)
|
43
|
+
# upload! does not yet honor "as" and similar scoping methods
|
44
|
+
upload! StringIO.new(generated_config_file), "/tmp/authorized_keys"
|
45
|
+
execute "mv", "-b", "/tmp/authorized_keys", "/home/#{name}/.ssh/authorized_keys"
|
46
|
+
execute "chown", "-R", "#{name}:#{name}", "/home/#{name}"
|
47
|
+
execute "chmod", "700", "/home/#{name}/.ssh"
|
48
|
+
execute "chmod", "600", "/home/#{name}/.ssh/authorized_keys"
|
49
|
+
end
|
50
|
+
|
51
|
+
def setup_file_permissions
|
52
|
+
ignore_options = fetch(:ignore_permissions_dirs).collect do |dir|
|
53
|
+
["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
54
|
+
end
|
55
|
+
chown_ignore_options = fetch(:webserver_owned_dirs).collect do |dir|
|
56
|
+
["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
57
|
+
end
|
58
|
+
# chown webserver owned
|
59
|
+
fetch(:webserver_owned_dirs).each do |dir|
|
60
|
+
if directory_exists?(dir)
|
61
|
+
execute "find", dir, ignore_options,
|
62
|
+
"-exec", "chown", "#{fetch(:webserver_username)}:#{fetch(:webserver_username)}", "{}", "+"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# chown
|
66
|
+
execute "find", fetch(:deploy_to), ignore_options, chown_ignore_options,
|
67
|
+
"-exec", "chown", "#{fetch(:deployment_username)}:#{fetch(:webserver_username)}", "{}", "+"
|
68
|
+
# chmod executable
|
69
|
+
fetch(:webserver_executable_dirs).each do |dir|
|
70
|
+
if directory_exists?(dir)
|
71
|
+
execute "find", dir, "-type", "f",
|
72
|
+
"-exec", "chmod", "0750", "{}", "+"
|
73
|
+
end
|
74
|
+
ignore_options += ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
75
|
+
end
|
76
|
+
# chmod writable
|
77
|
+
fetch(:webserver_writeable_dirs).each do |dir|
|
78
|
+
if directory_exists?(dir)
|
79
|
+
execute "find", "-L", dir, "-type", "d",
|
80
|
+
"-exec", "chmod", "2770", "{}", "+"
|
81
|
+
execute "find", "-L", dir, "-type", "f",
|
82
|
+
"-exec", "chmod", "0660", "{}", "+"
|
83
|
+
end
|
84
|
+
ignore_options += ["-not", "-path", "\"#{dir}\"", "-not", "-path", "\"#{dir}/*\""]
|
85
|
+
end
|
86
|
+
# chmod
|
87
|
+
execute "find", fetch(:deploy_to), "-type", "d", ignore_options,
|
88
|
+
"-exec", "chmod", "2750", "{}", "+"
|
89
|
+
execute "find", fetch(:deploy_to), "-type", "f", ignore_options,
|
90
|
+
"-exec", "chmod", "0640", "{}", "+"
|
91
|
+
end
|
92
|
+
|
139
93
|
def container_exists?(container_name)
|
140
94
|
test "bash", "-c", "\"docker", "inspect", container_name, "&>", "/dev/null\""
|
141
95
|
end
|
@@ -150,6 +104,30 @@ def container_is_restarting?(container_name)
|
|
150
104
|
container_name, "2>&1`\"", "=", "\"true\"", "]"
|
151
105
|
end
|
152
106
|
|
107
|
+
def check_stayed_running(name)
|
108
|
+
sleep 3
|
109
|
+
unless container_is_running?(name)
|
110
|
+
fatal "Container #{name} on #{fetch(:domain)} did not stay running more than 3 seconds"
|
111
|
+
exit
|
112
|
+
end
|
113
|
+
if container_is_restarting?(name)
|
114
|
+
fatal "Container #{name} on #{fetch(:domain)} is stuck restarting itself."
|
115
|
+
exit
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def start_container(name)
|
120
|
+
warn "Starting an existing but non-running container named #{name}"
|
121
|
+
execute("docker", "start", name)
|
122
|
+
check_stayed_running(name)
|
123
|
+
end
|
124
|
+
|
125
|
+
def restart_container(name)
|
126
|
+
warn "Restarting a running container named #{name}"
|
127
|
+
execute("docker", "restart", name)
|
128
|
+
check_stayed_running(name)
|
129
|
+
end
|
130
|
+
|
153
131
|
def localhost_port_responding?(port)
|
154
132
|
test "nc", "127.0.0.1", port, "<", "/dev/null", ">", "/dev/null;",
|
155
133
|
"[", "`echo", "$?`", "-eq", "0", "]"
|
@@ -163,10 +141,14 @@ def unix_user_add(user)
|
|
163
141
|
execute "adduser", "--disabled-password", "--gecos", "\"\"", user
|
164
142
|
end
|
165
143
|
|
166
|
-
def
|
144
|
+
def unix_user_get_uid(user)
|
167
145
|
capture("id", "-u", user).strip
|
168
146
|
end
|
169
147
|
|
148
|
+
def unix_user_get_gid(user)
|
149
|
+
capture("id", "-g", user).strip
|
150
|
+
end
|
151
|
+
|
170
152
|
def file_exists?(file)
|
171
153
|
test "[", "-f", file, "]"
|
172
154
|
end
|
@@ -175,32 +157,6 @@ def directory_exists?(dir)
|
|
175
157
|
test "[", "-d", dir, "]"
|
176
158
|
end
|
177
159
|
|
178
|
-
def
|
179
|
-
|
180
|
-
unless container_is_running?(name)
|
181
|
-
fatal "Container #{name} on #{fetch(:domain)} did not stay running more than 3 seconds"
|
182
|
-
exit
|
183
|
-
end
|
184
|
-
if container_is_restarting?(name)
|
185
|
-
fatal "Container #{name} on #{fetch(:domain)} is stuck restarting itself."
|
186
|
-
exit
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
def create_container(name, command)
|
191
|
-
warn "Starting a new container named #{name} on #{fetch(:domain)}"
|
192
|
-
execute("docker", "run", command)
|
193
|
-
check_stayed_running(name)
|
194
|
-
end
|
195
|
-
|
196
|
-
def start_container(name)
|
197
|
-
warn "Starting an existing but non-running container named #{name}"
|
198
|
-
execute("docker", "start", name)
|
199
|
-
check_stayed_running(name)
|
200
|
-
end
|
201
|
-
|
202
|
-
def restart_container(name)
|
203
|
-
warn "Restarting a running container named #{name}"
|
204
|
-
execute("docker", "restart", name)
|
205
|
-
check_stayed_running(name)
|
160
|
+
def files_in_directory?(dir)
|
161
|
+
test("[", "\"$(ls", "-A", "#{dir})\"", "]")
|
206
162
|
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.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,14 +9,14 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-01-
|
12
|
+
date: 2015-01-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: capistrano
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
21
|
version: 3.2.1
|
22
22
|
type: :runtime
|
@@ -24,10 +24,42 @@ dependencies:
|
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
26
|
requirements:
|
27
|
-
- -
|
27
|
+
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 3.2.1
|
30
|
-
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 10.3.2
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 10.3.2
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: sshkit
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.5.1
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.5.1
|
62
|
+
description: Deploy Ruby on Rails using Capistrano and Docker
|
31
63
|
email: davidamick@ctisolutionsinc.com
|
32
64
|
executables: []
|
33
65
|
extensions: []
|
@@ -38,6 +70,7 @@ files:
|
|
38
70
|
- lib/deployinator/check.rb
|
39
71
|
- lib/deployinator/config.rb
|
40
72
|
- lib/deployinator/helpers.rb
|
73
|
+
- lib/deployinator/built-in.rb
|
41
74
|
- lib/deployinator/examples/Capfile
|
42
75
|
- lib/deployinator/examples/config/deploy.rb
|
43
76
|
- lib/deployinator/examples/config/deploy/staging.rb
|
@@ -57,14 +90,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
57
90
|
requirements:
|
58
91
|
- - ! '>='
|
59
92
|
- !ruby/object:Gem::Version
|
60
|
-
version:
|
93
|
+
version: 1.9.3
|
61
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
95
|
none: false
|
63
96
|
requirements:
|
64
97
|
- - ! '>='
|
65
98
|
- !ruby/object:Gem::Version
|
66
99
|
version: '0'
|
67
|
-
requirements:
|
100
|
+
requirements:
|
101
|
+
- Docker ~> 1.3.1
|
68
102
|
rubyforge_project:
|
69
103
|
rubygems_version: 1.8.23.2
|
70
104
|
signing_key:
|