Class: EY::Serverside::DeployBase
Direct Known Subclasses
Deploy
Instance Attribute Summary
Attributes inherited from Task
#config
Instance Method Summary
collapse
#app_builds_own_assets?, #app_disables_assets?, #app_has_asset_task?, #app_needs_assets?, #bundled_rails_version, #compile_assets, #keep_existing_assets
#debug, #info, logfile, logfile=, #logged_system, verbose=, verbose?, #verbose?, #warning
Methods inherited from Task
#initialize, #require_custom_tasks, #roles, #run, #sudo
Instance Method Details
225
226
227
228
229
230
|
# File 'lib/engineyard-serverside/deploy.rb', line 225
def bundle
roles :app_master, :app, :solo, :util do
check_ruby_bundler
check_node_npm
end
end
|
#cached_deploy ⇒ Object
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
# File 'lib/engineyard-serverside/deploy.rb', line 20
def cached_deploy
debug "Deploying app from cached copy at #{Time.now.asctime}"
require_custom_tasks
push_code
info "~> Starting full deploy"
copy_repository_cache
check_repository
with_failed_release_cleanup do
create_revision_file
run_with_callbacks(:bundle)
setup_services
check_for_ey_config
symlink_configs
setup_sqlite3_if_necessary
conditionally_enable_maintenance_page
run_with_callbacks(:migrate)
run_with_callbacks(:compile_assets) callback(:before_symlink)
symlink
end
callback(:after_symlink)
run_with_callbacks(:restart)
disable_maintenance_page
cleanup_old_releases
debug "Finished deploy at #{Time.now.asctime}"
rescue Exception
debug "Finished failing to deploy at #{Time.now.asctime}"
puts_deploy_failure
raise
end
|
#callback(what) ⇒ Object
388
389
390
391
392
393
394
395
396
397
398
399
400
|
# File 'lib/engineyard-serverside/deploy.rb', line 388
def callback(what)
@callbacks_reached ||= true
if File.exist?("#{c.release_path}/deploy/#{what}.rb")
run Escape.shell_command(base_callback_command_for(what)) do |server, cmd|
per_instance_args = [
'--current-roles', server.roles.join(' '),
'--config', c.to_json,
]
per_instance_args << '--current-name' << server.name.to_s if server.name
cmd << " " << Escape.shell_command(per_instance_args)
end
end
end
|
#check_for_ey_config ⇒ Object
65
66
67
68
69
70
71
72
|
# File 'lib/engineyard-serverside/deploy.rb', line 65
def check_for_ey_config
if gemfile? && lockfile
configured_services = parse_configured_services
if !configured_services.empty? && !lockfile.has_ey_config?
warning "Gemfile.lock does not contain ey_config. Add it to get EY::Config access to: #{configured_services.keys.join(', ')}."
end
end
end
|
#check_repository ⇒ Object
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
# File 'lib/engineyard-serverside/deploy.rb', line 74
def check_repository
if gemfile?
info "~> Gemfile found."
if lockfile
info "~> Gemfile.lock found."
unless lockfile.any_database_adapter?
warning <<-WARN
Gemfile.lock does not contain a recognized database adapter.
A database-adapter gem such as mysql2, mysql, or do_mysql was expected.
This can prevent applications that use MySQL or PostreSQL from booting.
To fix, add any needed adapter to your Gemfile, bundle, commit, and redeploy.
Applications that don't use MySQL or PostgreSQL can safely ignore this warning.
WARN
end
else
warning <<-WARN
Gemfile.lock is missing!
You can get different versions of gems in production than what you tested with.
You can get different versions of gems on every deployment even if your Gemfile hasn't changed.
Deploying will take longer.
To fix this problem, commit your Gemfile.lock to your repository and redeploy.
WARN
end
else
info "~> No Gemfile. Deploying without bundler support."
end
end
|
#clean_environment ⇒ Object
GIT_SSH needs to be defined in the environment for customers with private bundler repos in their Gemfile.
188
189
190
|
# File 'lib/engineyard-serverside/deploy.rb', line 188
def clean_environment
%Q[export GIT_SSH="#{ssh_executable}" && export LANG="en_US.UTF-8" && unset RUBYOPT BUNDLE_PATH BUNDLE_FROZEN BUNDLE_WITHOUT BUNDLE_BIN BUNDLE_GEMFILE]
end
|
#clean_release_directory(dir, count = 3) ⇒ Object
Remove all but the most-recent count
releases from the specified release directory. IMPORTANT: This expects the release directory naming convention to be something with a sensible lexical order. Violate that at your peril.
242
243
244
245
246
247
248
|
# File 'lib/engineyard-serverside/deploy.rb', line 242
def clean_release_directory(dir, count = 3)
@cleanup_failed = true
ordinal = count.succ.to_s
info "~> Cleaning release directory: #{dir}"
sudo "ls -r #{dir} | tail -n +#{ordinal} | xargs -I@ rm -rf #{dir}/@"
@cleanup_failed = false
end
|
#cleanup_old_releases ⇒ Object
233
234
235
236
|
# File 'lib/engineyard-serverside/deploy.rb', line 233
def cleanup_old_releases
clean_release_directory(c.release_dir)
clean_release_directory(c.failed_release_dir)
end
|
#conditionally_enable_maintenance_page ⇒ Object
141
142
143
144
145
|
# File 'lib/engineyard-serverside/deploy.rb', line 141
def conditionally_enable_maintenance_page
if c.migrate? || required_downtime_stack?
enable_maintenance_page
end
end
|
#copy_repository_cache ⇒ Object
282
283
284
285
286
287
288
|
# File 'lib/engineyard-serverside/deploy.rb', line 282
def copy_repository_cache
info "~> Copying to #{c.release_path}"
run("mkdir -p #{c.release_path} #{c.failed_release_dir} && rsync -aq #{c.exclusions} #{c.repository_cache}/ #{c.release_path}")
info "~> Ensuring proper ownership."
sudo("chown -R #{c.user}:#{c.group} #{c.deploy_to}")
end
|
#create_revision_file ⇒ Object
290
291
292
|
# File 'lib/engineyard-serverside/deploy.rb', line 290
def create_revision_file
run create_revision_file_command
end
|
14
15
16
17
18
|
# File 'lib/engineyard-serverside/deploy.rb', line 14
def deploy
debug "Starting deploy at #{Time.now.asctime}"
update_repository_cache
cached_deploy
end
|
#disable_maintenance_page ⇒ Object
151
152
153
154
155
156
|
# File 'lib/engineyard-serverside/deploy.rb', line 151
def disable_maintenance_page
@maintenance_up = false
roles :app_master, :app, :solo do
run "rm -f #{File.join(c.shared_path, "system", "maintenance.html")}"
end
end
|
#enable_maintenance_page ⇒ Object
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
|
# File 'lib/engineyard-serverside/deploy.rb', line 111
def enable_maintenance_page
maintenance_page_candidates = [
"public/maintenance.html.custom",
"public/maintenance.html.tmp",
"public/maintenance.html",
"public/system/maintenance.html.default",
].map do |file|
File.join(c.latest_release, file)
end
maintenance_page_candidates << File.expand_path(
"default_maintenance_page.html",
File.dirname(__FILE__)
)
maintenance_file = maintenance_page_candidates.detect do |file|
File.exists?(file)
end
@maintenance_up = true
roles :app_master, :app, :solo do
maint_page_dir = File.join(c.shared_path, "system")
visible_maint_page = File.join(maint_page_dir, "maintenance.html")
run Escape.shell_command(['mkdir', '-p', maint_page_dir])
run Escape.shell_command(['cp', maintenance_file, visible_maint_page])
end
end
|
#generate_ssh_wrapper ⇒ Object
207
208
209
210
211
212
213
214
215
216
217
218
|
# File 'lib/engineyard-serverside/deploy.rb', line 207
def generate_ssh_wrapper
path = ssh_wrapper_path
identity_file = "~/.ssh/#{c.app}-deploy-key"
<<-WRAP
[[ -x #{path} ]] || cat > #{path} <<'SSH'
#!/bin/sh
unset SSH_AUTH_SOCK
ssh -o CheckHostIP=no -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o PasswordAuthentication=no -o LogLevel=DEBUG -o IdentityFile=#{identity_file} -o IdentitiesOnly=yes $*
SSH
chmod 0700 #{path}
WRAP
end
|
271
272
273
274
275
276
277
278
279
|
# File 'lib/engineyard-serverside/deploy.rb', line 271
def migrate
return unless c.migrate?
@migrations_reached = true
roles :app_master, :solo do
cmd = "cd #{c.release_path} && PATH=#{c.binstubs_path}:$PATH #{c.framework_envs} #{c.migration_command}"
info "~> Migrating: #{cmd}"
run(cmd)
end
end
|
57
58
59
60
61
62
63
|
# File 'lib/engineyard-serverside/deploy.rb', line 57
def parse_configured_services
result = YAML.load_file "#{c.shared_path}/config/ey_services_config_deploy.yml"
return {} unless result.is_a?(Hash)
result
rescue
{}
end
|
#required_downtime_stack? ⇒ Boolean
147
148
149
|
# File 'lib/engineyard-serverside/deploy.rb', line 147
def required_downtime_stack?
%w[ nginx_mongrel glassfish ].include? c.stack
end
|
174
175
176
177
178
179
180
181
|
# File 'lib/engineyard-serverside/deploy.rb', line 174
def restart
@restart_failed = true
info "~> Restarting app servers"
roles :app_master, :app, :solo do
run(restart_command)
end
@restart_failed = false
end
|
#restart_command ⇒ Object
183
184
185
|
# File 'lib/engineyard-serverside/deploy.rb', line 183
def restart_command
%{LANG="en_US.UTF-8" /engineyard/bin/app_#{c.app} deploy}
end
|
#restart_with_maintenance_page ⇒ Object
104
105
106
107
108
109
|
# File 'lib/engineyard-serverside/deploy.rb', line 104
def restart_with_maintenance_page
require_custom_tasks
conditionally_enable_maintenance_page
restart
disable_maintenance_page
end
|
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
|
# File 'lib/engineyard-serverside/deploy.rb', line 251
def rollback
if c.all_releases.size > 1
rolled_back_release = c.latest_release
c.release_path = c.previous_release(rolled_back_release)
revision = File.read(File.join(c.release_path, 'REVISION')).strip
info "~> Rolling back to previous release: #{short_log_message(revision)}"
run_with_callbacks(:symlink)
sudo "rm -rf #{rolled_back_release}"
bundle
info "~> Restarting with previous release."
with_maintenance_page { run_with_callbacks(:restart) }
else
info "~> Already at oldest release, nothing to roll back to."
exit(1)
end
end
|
#run_with_callbacks(task) ⇒ Object
158
159
160
161
162
|
# File 'lib/engineyard-serverside/deploy.rb', line 158
def run_with_callbacks(task)
callback("before_#{task}")
send(task)
callback("after_#{task}")
end
|
#services_command_check ⇒ Object
294
295
296
|
# File 'lib/engineyard-serverside/deploy.rb', line 294
def services_command_check
"which /usr/local/ey_resin/ruby/bin/ey-services-setup"
end
|
#services_setup_command ⇒ Object
298
299
300
|
# File 'lib/engineyard-serverside/deploy.rb', line 298
def services_setup_command
"/usr/local/ey_resin/ruby/bin/ey-services-setup #{config.app}"
end
|
#setup_services ⇒ Object
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
|
# File 'lib/engineyard-serverside/deploy.rb', line 302
def setup_services
info "~> Setting up external services."
previously_configured_services = parse_configured_services
begin
sudo(services_command_check)
rescue StandardError => e
info "Could not setup services. Upgrade your environment to get services configuration."
return
end
sudo(services_setup_command)
rescue StandardError => e
unless previously_configured_services.empty?
warning <<-WARNING
External services configuration not updated. Using previous version.
Deploy again if your services configuration appears incomplete or out of date.
#{e}
WARNING
end
end
|
#setup_sqlite3_if_necessary ⇒ Object
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
|
# File 'lib/engineyard-serverside/deploy.rb', line 322
def setup_sqlite3_if_necessary
if gemfile? && lockfile && lockfile.uses_sqlite3?
[
["Create databases directory if needed", "mkdir -p #{c.shared_path}/databases"],
["Creating SQLite database if needed", "touch #{c.shared_path}/databases/#{c.framework_env}.sqlite3"],
["Create config directory if needed", "mkdir -p #{c.release_path}/config"],
["Generating SQLite config", <<-WRAP],
cat > #{c.shared_path}/config/database.sqlite3.yml<<'YML'
#{c.framework_env}:
adapter: sqlite3
database: #{c.shared_path}/databases/#{c.framework_env}.sqlite3
pool: 5
timeout: 5000
YML
WRAP
["Symlink database.yml", "ln -nfs #{c.shared_path}/config/database.sqlite3.yml #{c.release_path}/config/database.yml"],
].each do |what, cmd|
info "~> #{what}"
run(cmd)
end
owner = [c.user, c.group].join(':')
info "~> Setting ownership to #{owner}"
sudo "chown -R #{owner} #{c.release_path}"
end
end
|
#ssh_executable ⇒ Object
If we don’t have a local version of the ssh wrapper script yet, create it on all the servers that will need it. TODO - This logic likely fails when people change deploy keys.
195
196
197
198
199
200
|
# File 'lib/engineyard-serverside/deploy.rb', line 195
def ssh_executable
roles :app_master, :app, :solo, :util do
run(generate_ssh_wrapper)
end
ssh_wrapper_path
end
|
#ssh_wrapper_path ⇒ Object
220
221
222
|
# File 'lib/engineyard-serverside/deploy.rb', line 220
def ssh_wrapper_path
"#{c.shared_path}/config/#{c.app}-ssh-wrapper"
end
|
#symlink(release_to_link = c.release_path) ⇒ Object
378
379
380
381
382
383
384
385
386
|
# File 'lib/engineyard-serverside/deploy.rb', line 378
def symlink(release_to_link=c.release_path)
info "~> Symlinking code."
run "rm -f #{c.current_path} && ln -nfs #{release_to_link} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
@symlink_changed = true
rescue Exception
sudo "rm -f #{c.current_path} && ln -nfs #{c.previous_release(release_to_link)} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
@symlink_changed = false
raise
end
|
#symlink_configs(release_to_link = c.release_path) ⇒ Object
349
350
351
352
353
354
355
356
357
358
|
# File 'lib/engineyard-serverside/deploy.rb', line 349
def symlink_configs(release_to_link=c.release_path)
info "~> Preparing shared resources for release."
symlink_tasks(release_to_link).each do |what, cmd|
info "~> #{what}"
run(cmd)
end
owner = [c.user, c.group].join(':')
info "~> Setting ownership to #{owner}"
sudo "chown -R #{owner} #{release_to_link}"
end
|
#symlink_tasks(release_to_link) ⇒ Object
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
|
# File 'lib/engineyard-serverside/deploy.rb', line 360
def symlink_tasks(release_to_link)
[
["Set group write permissions", "chmod -R g+w #{release_to_link}"],
["Remove revision-tracked shared directories from deployment", "rm -rf #{release_to_link}/log #{release_to_link}/public/system #{release_to_link}/tmp/pids"],
["Create tmp directory", "mkdir -p #{release_to_link}/tmp"],
["Symlink shared log directory", "ln -nfs #{c.shared_path}/log #{release_to_link}/log"],
["Create public directory if needed", "mkdir -p #{release_to_link}/public"],
["Create config directory if needed", "mkdir -p #{release_to_link}/config"],
["Create system directory if needed", "ln -nfs #{c.shared_path}/system #{release_to_link}/public/system"],
["Symlink shared pids directory", "ln -nfs #{c.shared_path}/pids #{release_to_link}/tmp/pids"],
["Symlink other shared config files", "find #{c.shared_path}/config -type f -not -name 'database.yml' -exec ln -s {} #{release_to_link}/config \\;"],
["Symlink mongrel_cluster.yml", "ln -nfs #{c.shared_path}/config/mongrel_cluster.yml #{release_to_link}/config/mongrel_cluster.yml"],
["Symlink database.yml", "ln -nfs #{c.shared_path}/config/database.yml #{release_to_link}/config/database.yml"],
["Symlink newrelic.yml if needed", "if [ -f \"#{c.shared_path}/config/newrelic.yml\" ]; then ln -nfs #{c.shared_path}/config/newrelic.yml #{release_to_link}/config/newrelic.yml; fi"],
]
end
|