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
226
227
228
229
230
231
|
# File 'lib/engineyard-serverside/deploy.rb', line 226
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
393
394
395
396
397
398
399
400
401
402
403
404
405
|
# File 'lib/engineyard-serverside/deploy.rb', line 393
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.
243
244
245
246
247
248
249
|
# File 'lib/engineyard-serverside/deploy.rb', line 243
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
234
235
236
237
|
# File 'lib/engineyard-serverside/deploy.rb', line 234
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
283
284
285
286
287
288
289
|
# File 'lib/engineyard-serverside/deploy.rb', line 283
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
291
292
293
|
# File 'lib/engineyard-serverside/deploy.rb', line 291
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
208
209
210
211
212
213
214
215
216
217
218
219
|
# File 'lib/engineyard-serverside/deploy.rb', line 208
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 'PasswordAuthentication no' -o 'LogLevel DEBUG' -o 'IdentityFile #{identity_file}' -o 'IdentitiesOnly yes' -o 'UserKnownHostsFile /dev/null' $*
SSH
chmod 0700 #{path}
WRAP
end
|
272
273
274
275
276
277
278
279
280
|
# File 'lib/engineyard-serverside/deploy.rb', line 272
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
|
#node_package_manager_command_check ⇒ Object
303
304
305
|
# File 'lib/engineyard-serverside/deploy.rb', line 303
def node_package_manager_command_check
"which npm"
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
|
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
|
# File 'lib/engineyard-serverside/deploy.rb', line 252
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
295
296
297
|
# File 'lib/engineyard-serverside/deploy.rb', line 295
def services_command_check
"which /usr/local/ey_resin/ruby/bin/ey-services-setup"
end
|
#services_setup_command ⇒ Object
299
300
301
|
# File 'lib/engineyard-serverside/deploy.rb', line 299
def services_setup_command
"/usr/local/ey_resin/ruby/bin/ey-services-setup #{config.app}"
end
|
#setup_services ⇒ Object
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
|
# File 'lib/engineyard-serverside/deploy.rb', line 307
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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
|
# File 'lib/engineyard-serverside/deploy.rb', line 327
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
201
|
# File 'lib/engineyard-serverside/deploy.rb', line 195
def ssh_executable
path = ssh_wrapper_path
roles :app_master, :app, :solo, :util do
run(generate_ssh_wrapper)
end
path
end
|
#ssh_wrapper_path ⇒ Object
221
222
223
|
# File 'lib/engineyard-serverside/deploy.rb', line 221
def ssh_wrapper_path
"#{c.shared_path}/config/#{c.app}-ssh-wrapper"
end
|
#symlink(release_to_link = c.release_path) ⇒ Object
383
384
385
386
387
388
389
390
391
|
# File 'lib/engineyard-serverside/deploy.rb', line 383
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
354
355
356
357
358
359
360
361
362
363
|
# File 'lib/engineyard-serverside/deploy.rb', line 354
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
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
|
# File 'lib/engineyard-serverside/deploy.rb', line 365
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
|