deployinator 0.1.2 → 0.1.3
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 +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:
|