ey-deploy 0.4.0 → 0.4.1

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.
@@ -20,6 +20,8 @@ module EY
20
20
  @dna_json ||= `sudo cat /etc/chef/dna.json`
21
21
  end
22
22
 
23
+ RemoteFailure = Class.new StandardError
24
+
23
25
  private
24
26
  def self.deep_indifferentize(thing)
25
27
  if thing.kind_of?(Hash)
@@ -4,6 +4,12 @@ require 'thor'
4
4
 
5
5
  module EY
6
6
  class CLI < Thor
7
+ def self.start(*)
8
+ super
9
+ rescue RemoteFailure
10
+ exit(1)
11
+ end
12
+
7
13
  method_option :migrate, :type => :string,
8
14
  :desc => "Run migrations with this deploy",
9
15
  :aliases => ["-m"]
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html >
2
+ <html>
3
+ <head>
4
+ <title>Undergoing Maintenance</title>
5
+
6
+ <style type="text/css">
7
+ body{font-size: 75%;font-family: Helvetica,Arial,sans-serif;color:#333;background:#FDFDFD;}
8
+ .container{font-size:1.1em;width:700px;text-align:center;margin:80px auto;}
9
+ .message{background:#FFF;border:1px solid #EEE;padding:30px;}
10
+ h1{margin:0 0 0.5em;font-weight:normal;}
11
+ p{margin:0;line-height:1.75em;}
12
+ .foot{color:#999;margin-top:30px}
13
+ .foot a{color:#666;text-decoration:none}
14
+ </style>
15
+ </head>
16
+
17
+ <body>
18
+ <div class="container">
19
+ <div class="message">
20
+ <h1>This site is currently undergoing some maintenance.</h1>
21
+ <p>Customize this maintenance page by updating the file located at: /public/maintenance.html</p>
22
+ </div>
23
+ <div class="foot">
24
+ Hosted by <a href="http://www.engineyard.com">Engine Yard</a>
25
+ </div>
26
+ </div>
27
+ </body>
28
+
29
+ </html>
@@ -11,39 +11,66 @@ module EY
11
11
  push_code
12
12
 
13
13
  puts "~> Starting full deploy"
14
-
15
14
  copy_repository_cache
16
- create_revision_file
17
- bundle
18
- symlink_configs
19
15
 
20
- with_maintenance_page do
16
+ with_failed_release_cleanup do
17
+ create_revision_file
18
+ bundle
19
+ symlink_configs
20
+ conditionally_enable_maintenance_page
21
21
  run_with_callbacks(:migrate)
22
- run_with_callbacks(:symlink)
23
- run_with_callbacks(:restart)
22
+ callback(:before_symlink)
23
+ symlink
24
24
  end
25
25
 
26
- cleanup
26
+ callback(:after_symlink)
27
+ run_with_callbacks(:restart)
28
+ disable_maintenance_page
29
+
30
+ cleanup_old_releases
27
31
 
28
32
  puts "~> finalizing deploy"
33
+ rescue Exception
34
+ puts_deploy_failure
35
+ raise
29
36
  end
30
37
 
31
38
  def enable_maintenance_page
32
- if c.migrate? || c.stack == "nginx_mongrel"
33
- # put in the maintenance page
34
- maintenance_file = ["public/maintenance.html.custom", "public/maintenance.html.tmp", "public/maintenance.html", "public/system/maintenance.html.default"].detect do |file|
35
- File.exists?(File.join(c.latest_release, file))
36
- end
39
+ maintenance_page_candidates = [
40
+ "public/maintenance.html.custom",
41
+ "public/maintenance.html.tmp",
42
+ "public/maintenance.html",
43
+ "public/system/maintenance.html.default",
44
+ ].map do |file|
45
+ File.join(c.latest_release, file)
46
+ end
37
47
 
38
- if maintenance_file
39
- roles :app_master, :app, :solo do
40
- run "cp #{File.join(c.latest_release, maintenance_file)} #{File.join(c.shared_path, "system", "maintenance.html")}"
41
- end
42
- end
48
+ # this one is guaranteed to exist
49
+ maintenance_page_candidates << File.expand_path(
50
+ "default_maintenance_page.html",
51
+ File.dirname(__FILE__)
52
+ )
53
+
54
+ # put in the maintenance page
55
+ maintenance_file = maintenance_page_candidates.detect do |file|
56
+ File.exists?(file)
57
+ end
58
+
59
+ @maintenance_up = true
60
+ roles :app_master, :app, :solo do
61
+ visible_maint_page = File.join(c.shared_path, "system", "maintenance.html")
62
+ run "cp '#{maintenance_file}' '#{visible_maint_page}'"
63
+ end
64
+ end
65
+
66
+ def conditionally_enable_maintenance_page
67
+ if c.migrate? || c.stack == "nginx_mongrel"
68
+ enable_maintenance_page
43
69
  end
44
70
  end
45
71
 
46
72
  def disable_maintenance_page
73
+ @maintenance_up = false
47
74
  roles :app_master, :app, :solo do
48
75
  run "rm -f #{File.join(c.shared_path, "system", "maintenance.html")}"
49
76
  end
@@ -65,6 +92,7 @@ module EY
65
92
 
66
93
  # task
67
94
  def restart
95
+ @restart_failed = true
68
96
  puts "~> Restarting app servers"
69
97
  puts "~> restarting app: #{c.latest_release}"
70
98
  roles :app_master, :app, :solo do
@@ -77,6 +105,7 @@ module EY
77
105
  sudo("touch #{c.latest_release}/tmp/restart.txt")
78
106
  end
79
107
  end
108
+ @restart_failed = false
80
109
  end
81
110
 
82
111
  # task
@@ -84,37 +113,44 @@ module EY
84
113
  roles :app_master, :app, :solo do
85
114
  if File.exist?("#{c.latest_release}/Gemfile")
86
115
  puts "~> Gemfile detected, bundling gems"
87
- run %|cd #{c.latest_release} && bundle install|
116
+ run "cd #{c.latest_release} && bundle install"
88
117
  end
89
118
  end
90
119
  end
91
120
 
92
121
  # task
93
- def cleanup
122
+ def cleanup_old_releases
123
+ @cleanup_failed = true
94
124
  puts "~> cleaning up old releases"
95
125
  sudo "ls #{c.release_dir} | head -n -3 | xargs -I{} rm -rf #{c.release_dir}/{}"
126
+ @cleanup_failed = false
96
127
  end
97
128
 
98
129
  # task
99
130
  def rollback
100
- puts "~> rolling back to previous release"
101
- c.release_path = c.previous_release
102
- run_with_callbacks(:symlink, c.previous_release)
103
- FileUtils.rm_rf c.latest_release
104
- bundle
105
- puts "~> restarting with previous release"
106
- with_maintenance_page { run_with_callbacks(:restart) }
131
+ if c.all_releases.size > 1
132
+ puts "~> rolling back to previous release"
133
+ c.release_path = c.previous_release
134
+ run_with_callbacks(:symlink, c.previous_release)
135
+ cleanup_latest_release
136
+ bundle
137
+ puts "~> restarting with previous release"
138
+ with_maintenance_page { run_with_callbacks(:restart) }
139
+ else
140
+ puts "~> Already at oldest release, nothing to roll back to"
141
+ exit(1)
142
+ end
107
143
  end
108
144
 
109
145
  # task
110
146
  def migrate
147
+ return unless c.migrate?
148
+ @migrations_reached = true
111
149
  roles :app_master, :solo do
112
- if c.migrate?
113
- puts "~> migrating"
114
- cmd = "cd #{c.latest_release} && #{c.framework_envs} #{c.migration_command}"
115
- puts "~> Migrating: #{cmd}"
116
- run(cmd)
117
- end
150
+ puts "~> migrating"
151
+ cmd = "cd #{c.latest_release} && #{c.framework_envs} #{c.migration_command}"
152
+ puts "~> Migrating: #{cmd}"
153
+ run(cmd)
118
154
  end
119
155
  end
120
156
 
@@ -150,31 +186,58 @@ module EY
150
186
  # task
151
187
  def symlink(release_to_link=c.latest_release)
152
188
  puts "~> symlinking code"
153
- begin
154
- sudo "rm -f #{c.current_path} && ln -nfs #{release_to_link} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
155
- rescue => e
156
- 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}"
157
- sudo "rm -rf #{release_to_link}"
158
- raise e
159
- end
189
+ sudo "rm -f #{c.current_path} && ln -nfs #{release_to_link} #{c.current_path} && chown -R #{c.user}:#{c.group} #{c.current_path}"
190
+ @symlink_changed = true
191
+ rescue Exception
192
+ 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}"
193
+ @symlink_changed = false
194
+ raise
160
195
  end
161
196
 
162
197
  def callback(what)
198
+ @callbacks_reached ||= true
163
199
  if File.exist?("#{c.latest_release}/deploy/#{what}.rb")
164
200
  eysd_path = $0 # invoke others just like we were invoked
165
- EY::Server.all.each do |server|
166
- server.run("#{eysd_path} hook '#{what}' --app '#{config.app}' --release-path #{config.release_path}")
167
- end
201
+ run "#{eysd_path} hook '#{what}' --app '#{config.app}' --release-path #{config.release_path}"
168
202
  end
169
203
  end
170
204
 
171
205
  protected
172
206
 
207
+ def puts_deploy_failure
208
+ if @cleanup_failed
209
+ puts "~> [Relax] Your site is running new code, but cleaning up old deploys failed"
210
+ elsif @maintenance_up
211
+ puts "~> [Attention] Maintenance page still up, consider the following before removing:"
212
+ puts " * any deploy hooks ran, be careful if they were destructive" if @callbacks_reached
213
+ puts " * any migrations ran, be careful if they were destructive" if @migrations_reached
214
+ if @symlink_changed
215
+ puts " * your new code is symlinked as current"
216
+ else
217
+ puts " * your old code is still symlinked as current"
218
+ end
219
+ puts " * application servers failed to restart" if @restart_failed
220
+ else
221
+ puts "~> [Relax] Your site is still running old code and nothing destructive could have occurred"
222
+ end
223
+ end
224
+
173
225
  def with_maintenance_page
174
- enable_maintenance_page
226
+ conditionally_enable_maintenance_page
175
227
  yield if block_given?
176
228
  disable_maintenance_page
177
229
  end
230
+
231
+ def with_failed_release_cleanup
232
+ yield
233
+ rescue Exception
234
+ cleanup_latest_release
235
+ raise
236
+ end
237
+
238
+ def cleanup_latest_release
239
+ sudo "rm -rf #{c.latest_release}"
240
+ end
178
241
  end
179
242
 
180
243
  class Deploy < DeployBase
@@ -80,7 +80,7 @@ module EY
80
80
  end
81
81
 
82
82
  def ssh_command
83
- "ssh -i /home/#{config.user}/.ssh/internal"
83
+ "ssh -i /home/#{config.user}/.ssh/internal -o StrictHostKeyChecking=no -o PasswordAuthentication=no"
84
84
  end
85
85
  end
86
86
  end
@@ -37,15 +37,11 @@ module EY
37
37
  end
38
38
 
39
39
  def run(cmd)
40
- EY::Server.from_roles(@roles).each do |server|
41
- server.run prepare_run(cmd)
42
- end
40
+ run_on_roles { prepare_run cmd }
43
41
  end
44
42
 
45
43
  def sudo(cmd)
46
- EY::Server.from_roles(@roles).each do |server|
47
- server.run prepare_sudo(cmd)
48
- end
44
+ run_on_roles { prepare_sudo cmd }
49
45
  end
50
46
 
51
47
  def prepare_run(command)
@@ -55,5 +51,15 @@ module EY
55
51
  def prepare_sudo(command)
56
52
  Escape.shell_command ["sudo", "sh", "-l", "-c", command]
57
53
  end
54
+
55
+ private
56
+
57
+ def run_on_roles
58
+ cmd = yield
59
+ EY::Server.from_roles(@roles).inject(false) do |acc, server|
60
+ failure = !server.run(cmd)
61
+ acc || failure
62
+ end && raise(EY::RemoteFailure)
63
+ end
58
64
  end
59
65
  end
@@ -1,3 +1,3 @@
1
1
  module EY
2
- VERSION = '0.4.0'
2
+ VERSION = '0.4.1'
3
3
  end
@@ -18,17 +18,17 @@ describe "the EY::Deploy API" do
18
18
  @call_order = []
19
19
  end
20
20
 
21
- def push_code() @call_order << 'push_code' end
22
- def copy_repository_cache() @call_order << 'copy_repository_cache' end
23
- def create_revision_file() @call_order << 'create_revision_file' end
24
- def bundle() @call_order << 'bundle' end
25
- def symlink_configs() @call_order << 'symlink_configs' end
26
- def migrate() @call_order << 'migrate' end
27
- def symlink() @call_order << 'symlink' end
28
- def restart() @call_order << 'restart' end
29
- def cleanup() @call_order << 'cleanup' end
30
- def enable_maintenance_page() @call_order << 'enable_maintenance_page' end
31
- def disable_maintenance_page() @call_order << 'disable_maintenance_page' end
21
+ def push_code() @call_order << 'push_code' end
22
+ def copy_repository_cache() @call_order << 'copy_repository_cache' end
23
+ def create_revision_file() @call_order << 'create_revision_file' end
24
+ def bundle() @call_order << 'bundle' end
25
+ def symlink_configs() @call_order << 'symlink_configs' end
26
+ def migrate() @call_order << 'migrate' end
27
+ def symlink() @call_order << 'symlink' end
28
+ def restart() @call_order << 'restart' end
29
+ def cleanup_old_releases() @call_order << 'cleanup_old_releases' end
30
+ def conditionally_enable_maintenance_page() @call_order << 'conditionally_enable_maintenance_page' end
31
+ def disable_maintenance_page() @call_order << 'disable_maintenance_page' end
32
32
  end
33
33
 
34
34
  td = TestDeploy.new(EY::Deploy::Configuration.new)
@@ -39,12 +39,12 @@ describe "the EY::Deploy API" do
39
39
  create_revision_file
40
40
  bundle
41
41
  symlink_configs
42
- enable_maintenance_page
42
+ conditionally_enable_maintenance_page
43
43
  migrate
44
44
  symlink
45
45
  restart
46
46
  disable_maintenance_page
47
- cleanup)
47
+ cleanup_old_releases)
48
48
  end
49
49
 
50
50
  describe "task overrides" do
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 4
8
- - 0
9
- version: 0.4.0
8
+ - 1
9
+ version: 0.4.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - EY Cloud Team
@@ -14,25 +14,23 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-06-11 00:00:00 -07:00
17
+ date: 2010-06-16 00:00:00 -07:00
18
18
  default_executable: eysd
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- prerelease: false
22
- name: rake
23
- requirement: &id001 !ruby/object:Gem::Requirement
21
+ version_requirements: &id001 !ruby/object:Gem::Requirement
24
22
  requirements:
25
23
  - - ">="
26
24
  - !ruby/object:Gem::Version
27
25
  segments:
28
26
  - 0
29
27
  version: "0"
28
+ name: rake
29
+ prerelease: false
30
+ requirement: *id001
30
31
  type: :runtime
31
- version_requirements: *id001
32
32
  - !ruby/object:Gem::Dependency
33
- prerelease: false
34
- name: escape
35
- requirement: &id002 !ruby/object:Gem::Requirement
33
+ version_requirements: &id002 !ruby/object:Gem::Requirement
36
34
  requirements:
37
35
  - - ">="
38
36
  - !ruby/object:Gem::Version
@@ -41,20 +39,22 @@ dependencies:
41
39
  - 0
42
40
  - 4
43
41
  version: 0.0.4
42
+ name: escape
43
+ prerelease: false
44
+ requirement: *id002
44
45
  type: :runtime
45
- version_requirements: *id002
46
46
  - !ruby/object:Gem::Dependency
47
- prerelease: false
48
- name: json
49
- requirement: &id003 !ruby/object:Gem::Requirement
47
+ version_requirements: &id003 !ruby/object:Gem::Requirement
50
48
  requirements:
51
49
  - - ">="
52
50
  - !ruby/object:Gem::Version
53
51
  segments:
54
52
  - 0
55
53
  version: "0"
54
+ name: json
55
+ prerelease: false
56
+ requirement: *id003
56
57
  type: :runtime
57
- version_requirements: *id003
58
58
  description:
59
59
  email: cloud@engineyard.com
60
60
  executables:
@@ -67,6 +67,7 @@ files:
67
67
  - bin/eysd
68
68
  - lib/ey-deploy/cli.rb
69
69
  - lib/ey-deploy/configuration.rb
70
+ - lib/ey-deploy/default_maintenance_page.html
70
71
  - lib/ey-deploy/deploy.rb
71
72
  - lib/ey-deploy/deploy_hook.rb
72
73
  - lib/ey-deploy/server.rb