ey-deploy 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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