sunshine 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +26 -0
- data/Manifest.txt +1 -0
- data/bin/sunshine +8 -1
- data/lib/commands/list.rb +11 -6
- data/lib/commands/restart.rb +6 -5
- data/lib/commands/rm.rb +6 -5
- data/lib/commands/script.rb +5 -4
- data/lib/commands/start.rb +7 -6
- data/lib/commands/stop.rb +6 -5
- data/lib/sunshine.rb +68 -50
- data/lib/sunshine/app.rb +269 -55
- data/lib/sunshine/daemon.rb +72 -18
- data/lib/sunshine/dependency_lib.rb +0 -2
- data/lib/sunshine/exceptions.rb +35 -17
- data/lib/sunshine/package_managers/apt.rb +1 -1
- data/lib/sunshine/package_managers/dependency.rb +0 -5
- data/lib/sunshine/package_managers/yum.rb +1 -1
- data/lib/sunshine/remote_shell.rb +21 -5
- data/lib/sunshine/repo.rb +5 -6
- data/lib/sunshine/repos/rsync_repo.rb +1 -1
- data/lib/sunshine/server_app.rb +146 -14
- data/lib/sunshine/shell.rb +49 -24
- data/lib/sunshine/trap_stack.rb +54 -0
- data/templates/nginx/nginx.conf.erb +24 -0
- data/test/helper_methods.rb +1 -1
- data/test/unit/test_app.rb +48 -6
- data/test/unit/test_daemon.rb +3 -3
- data/test/unit/test_nginx.rb +7 -4
- data/test/unit/test_server.rb +6 -6
- data/test/unit/test_server_app.rb +107 -9
- metadata +8 -7
data/History.txt
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
=== 1.2.1 / 2010-10-05
|
2
|
+
|
3
|
+
* Improvements:
|
4
|
+
|
5
|
+
* Added customizable behaviors for sigint and failures during deploys.
|
6
|
+
|
7
|
+
* Added Sunshine irb shell to work directly with deploying apps.
|
8
|
+
|
9
|
+
* Added ability to launch a pseudo terminal from a RemoteShell.
|
10
|
+
|
11
|
+
* Added exit code to CmdError.
|
12
|
+
|
13
|
+
* Added exclude paths for checkout by copy.
|
14
|
+
|
15
|
+
* Bugfixes:
|
16
|
+
|
17
|
+
* Fixed custom scripts.
|
18
|
+
|
19
|
+
* Fixed deploy env defaults.
|
20
|
+
|
21
|
+
* Removed Shell#update_timeout as it was unnecessary.
|
22
|
+
|
23
|
+
* RemoteShell rsync recurses by default.
|
24
|
+
|
25
|
+
* Fixed start and stop script permissions when calling sunshine commands.
|
26
|
+
|
1
27
|
=== 1.2.0 / 2010-09-08
|
2
28
|
|
3
29
|
* Improvements:
|
data/Manifest.txt
CHANGED
@@ -46,6 +46,7 @@ lib/sunshine/repos/rsync_repo.rb
|
|
46
46
|
lib/sunshine/repos/svn_repo.rb
|
47
47
|
lib/sunshine/server_app.rb
|
48
48
|
lib/sunshine/shell.rb
|
49
|
+
lib/sunshine/trap_stack.rb
|
49
50
|
templates/apache/apache.conf.erb
|
50
51
|
templates/mongrel_rails/mongrel_rails.conf.erb
|
51
52
|
templates/nginx/nginx.conf.erb
|
data/bin/sunshine
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby -w
|
2
2
|
|
3
|
-
|
3
|
+
begin
|
4
|
+
require 'sunshine'
|
5
|
+
rescue LoadError => e
|
6
|
+
raise e unless e.message =~ %r{no such file to load -- sunshine}
|
7
|
+
|
8
|
+
$: << File.join(File.dirname(__FILE__), "../lib")
|
9
|
+
require 'sunshine'
|
10
|
+
end
|
4
11
|
|
5
12
|
Sunshine.run
|
data/lib/commands/list.rb
CHANGED
@@ -16,6 +16,7 @@ module Sunshine
|
|
16
16
|
# -f, --format FORMAT Set the output format (txt, yml, json)
|
17
17
|
# -u, --user USER User to use for remote login. Use with -r
|
18
18
|
# -r, --remote svr1,svr2 Run on one or more remote servers.
|
19
|
+
# -S, --sudo Run remote commands using sudo or sudo -u USER
|
19
20
|
# -v, --verbose Run in verbose mode.
|
20
21
|
|
21
22
|
class ListCommand < DefaultCommand
|
@@ -176,7 +177,11 @@ module Sunshine
|
|
176
177
|
|
177
178
|
def status(*app_names)
|
178
179
|
each_app(*app_names) do |server_app|
|
179
|
-
|
180
|
+
begin
|
181
|
+
server_app.status
|
182
|
+
rescue => e
|
183
|
+
e.message
|
184
|
+
end
|
180
185
|
end
|
181
186
|
end
|
182
187
|
|
@@ -185,17 +190,17 @@ module Sunshine
|
|
185
190
|
# Runs a command and returns the status for each app_name:
|
186
191
|
# status_after_command 'restart', ['app1', 'app2']
|
187
192
|
|
188
|
-
def status_after_command cmd, app_names
|
193
|
+
def status_after_command cmd, app_names, options=nil
|
189
194
|
each_app(*app_names) do |server_app|
|
190
195
|
|
191
196
|
yield(server_app) if block_given?
|
192
197
|
|
193
198
|
begin
|
194
|
-
server_app.run_script cmd
|
195
|
-
server_app.
|
199
|
+
server_app.run_script! cmd, options
|
200
|
+
server_app.status.to_s
|
196
201
|
|
197
202
|
rescue CmdError => e
|
198
|
-
raise "Failed running #{cmd}: #{server_app.status}"
|
203
|
+
raise "Failed running #{cmd}: #{server_app.status rescue :not_found}"
|
199
204
|
end
|
200
205
|
end
|
201
206
|
end
|
@@ -268,7 +273,7 @@ module Sunshine
|
|
268
273
|
# Load the app list yaml file from the server.
|
269
274
|
|
270
275
|
def self.load_list server
|
271
|
-
yml_list = server.call "cat #{Sunshine::APP_LIST_PATH} || echo
|
276
|
+
yml_list = server.call "cat #{Sunshine::APP_LIST_PATH} || echo"
|
272
277
|
|
273
278
|
list = YAML.load yml_list
|
274
279
|
list = {} unless Hash === list
|
data/lib/commands/restart.rb
CHANGED
@@ -9,10 +9,11 @@ module Sunshine
|
|
9
9
|
# app_name Name of the application to restart.
|
10
10
|
#
|
11
11
|
# Options:
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# -f, --format FORMAT Set the output format (txt, yml, json)
|
13
|
+
# -u, --user USER User to use for remote login. Use with -r.
|
14
|
+
# -r, --remote svr1,svr2 Run on one or more remote servers.
|
15
|
+
# -S, --sudo Run remote commands using sudo or sudo -u USER
|
16
|
+
# -v, --verbose Run in verbose mode.
|
16
17
|
|
17
18
|
class RestartCommand < ListCommand
|
18
19
|
|
@@ -38,7 +39,7 @@ module Sunshine
|
|
38
39
|
# Restart specified apps.
|
39
40
|
|
40
41
|
def restart app_names
|
41
|
-
status_after_command :restart, app_names
|
42
|
+
status_after_command :restart, app_names, :sudo => false
|
42
43
|
end
|
43
44
|
|
44
45
|
|
data/lib/commands/rm.rb
CHANGED
@@ -9,11 +9,12 @@ module Sunshine
|
|
9
9
|
# app_name Name of the application to remove.
|
10
10
|
#
|
11
11
|
# Options:
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
12
|
+
# -d, --delete Delete the app directory.
|
13
|
+
# -f, --format FORMAT Set the output format (txt, yml, json)
|
14
|
+
# -u, --user USER User to use for remote login. Use with -r.
|
15
|
+
# -r, --remote svr1,svr2 Run on one or more remote servers.
|
16
|
+
# -S, --sudo Run remote commands using sudo or sudo -u USER
|
17
|
+
# -v, --verbose Run in verbose mode.
|
17
18
|
|
18
19
|
class RmCommand < ListCommand
|
19
20
|
|
data/lib/commands/script.rb
CHANGED
@@ -10,10 +10,11 @@ module Sunshine
|
|
10
10
|
# app_name Name of the application to run script for.
|
11
11
|
#
|
12
12
|
# Options:
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
13
|
+
# -f, --format FORMAT Set the output format (txt, yml, json)
|
14
|
+
# -u, --user USER User to use for remote login. Use with -r.
|
15
|
+
# -r, --remote svr1,svr2 Run on one or more remote servers.
|
16
|
+
# -S, --sudo Run remote commands using sudo or sudo -u USER
|
17
|
+
# -v, --verbose Run in verbose mode.
|
17
18
|
|
18
19
|
class ScriptCommand < ListCommand
|
19
20
|
|
data/lib/commands/start.rb
CHANGED
@@ -9,11 +9,12 @@ module Sunshine
|
|
9
9
|
# app_name Name of the application to start.
|
10
10
|
#
|
11
11
|
# Options:
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
12
|
+
# -F, --force Stop apps that are running, then start them.
|
13
|
+
# -f, --format FORMAT Set the output format (txt, yml, json)
|
14
|
+
# -u, --user USER User to use for remote login. Use with -r.
|
15
|
+
# -r, --remote svr1,svr2 Run on one or more remote servers.
|
16
|
+
# -S, --sudo Run remote commands using sudo or sudo -u USER
|
17
|
+
# -v, --verbose Run in verbose mode.
|
17
18
|
|
18
19
|
class StartCommand < ListCommand
|
19
20
|
|
@@ -41,7 +42,7 @@ module Sunshine
|
|
41
42
|
# Start specified apps.
|
42
43
|
|
43
44
|
def start app_names, force=false
|
44
|
-
status_after_command :start, app_names do |server_app|
|
45
|
+
status_after_command :start, app_names, :sudo => false do |server_app|
|
45
46
|
|
46
47
|
server_app.stop if server_app.running? && force
|
47
48
|
end
|
data/lib/commands/stop.rb
CHANGED
@@ -9,10 +9,11 @@ module Sunshine
|
|
9
9
|
# app_name Name of the application to stop.
|
10
10
|
#
|
11
11
|
# Options:
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# -f, --format FORMAT Set the output format (txt, yml, json)
|
13
|
+
# -u, --user USER User to use for remote login. Use with -r.
|
14
|
+
# -r, --remote svr1,svr2 Run on one or more remote servers.
|
15
|
+
# -S, --sudo Run remote commands using sudo or sudo -u USER
|
16
|
+
# -v, --verbose Run in verbose mode.
|
16
17
|
|
17
18
|
class StopCommand < ListCommand
|
18
19
|
|
@@ -38,7 +39,7 @@ module Sunshine
|
|
38
39
|
# Stop specified apps.
|
39
40
|
|
40
41
|
def stop app_names
|
41
|
-
status_after_command :stop, app_names
|
42
|
+
status_after_command :stop, app_names, :sudo => false
|
42
43
|
end
|
43
44
|
|
44
45
|
##
|
data/lib/sunshine.rb
CHANGED
@@ -11,6 +11,12 @@ require 'optparse'
|
|
11
11
|
require 'time'
|
12
12
|
require 'fileutils'
|
13
13
|
require 'tmpdir'
|
14
|
+
require 'irb'
|
15
|
+
|
16
|
+
|
17
|
+
# Turn off EOF tracking to be able to prompt on deploy exceptions.
|
18
|
+
HighLine.track_eof = false
|
19
|
+
|
14
20
|
|
15
21
|
##
|
16
22
|
# Main module, used for configuration and running commands.
|
@@ -19,7 +25,7 @@ module Sunshine
|
|
19
25
|
|
20
26
|
##
|
21
27
|
# Sunshine version.
|
22
|
-
VERSION = '1.2.
|
28
|
+
VERSION = '1.2.1'
|
23
29
|
|
24
30
|
##
|
25
31
|
# Path to the list of installed sunshine apps.
|
@@ -36,18 +42,16 @@ module Sunshine
|
|
36
42
|
##
|
37
43
|
# Default configuration.
|
38
44
|
DEFAULT_CONFIG = {
|
39
|
-
'
|
45
|
+
'interactive' => true,
|
40
46
|
'auto_dependencies' => true,
|
41
|
-
'deploy_env' =>
|
42
|
-
|
43
|
-
|
44
|
-
ENV['RACK_ENV'] ||
|
45
|
-
ENV['RAILS_ENV'] ||
|
46
|
-
:development ),
|
47
|
+
'deploy_env' => :development,
|
48
|
+
'exception_behavior' => :revert,
|
49
|
+
'exclude_paths' => [],
|
47
50
|
'level' => 'info',
|
48
51
|
'max_deploy_versions' => 5,
|
49
52
|
'remote_checkouts' => false,
|
50
53
|
'timeout' => 300,
|
54
|
+
'sigint_behavior' => :revert,
|
51
55
|
'web_directory' => '/srv/http'
|
52
56
|
}
|
53
57
|
|
@@ -113,11 +117,34 @@ module Sunshine
|
|
113
117
|
|
114
118
|
|
115
119
|
##
|
116
|
-
#
|
117
|
-
#
|
120
|
+
# Defines what to do when deploy raises an exception.
|
121
|
+
# Supported values are:
|
122
|
+
# ::revert: Revert to the previous deploy.
|
123
|
+
# ::console: Start an interactive ruby shell within the app's context.
|
124
|
+
# ::exit: Stop deploy and exit, leaving deploy in unfinished state.
|
125
|
+
# ::prompt: Ask what to do.
|
126
|
+
# Defaults to :revert. Overridden in the config.
|
127
|
+
|
128
|
+
def self.exception_behavior
|
129
|
+
@config['exception_behavior']
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
##
|
134
|
+
# Array of paths or globs that should be excluded from the checkout.
|
135
|
+
# Does not work with remote_checkouts enabled.
|
136
|
+
|
137
|
+
def self.exclude_paths
|
138
|
+
@config['exclude_paths']
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
##
|
143
|
+
# Should sunshine ever ask for user input? True by default.
|
144
|
+
# Overridden in the config or with the -a option.
|
118
145
|
|
119
146
|
def self.interactive?
|
120
|
-
|
147
|
+
@config['interactive']
|
121
148
|
end
|
122
149
|
|
123
150
|
|
@@ -155,6 +182,20 @@ module Sunshine
|
|
155
182
|
end
|
156
183
|
|
157
184
|
|
185
|
+
##
|
186
|
+
# Defines what to do when sigint is sent during deploys.
|
187
|
+
# Supported values are:
|
188
|
+
# ::revert: Revert to the previous deploy.
|
189
|
+
# ::console: Start an interactive ruby shell within the app's context.
|
190
|
+
# ::exit: Stop deploy and exit, leaving deploy in unfinished state.
|
191
|
+
# ::prompt: Ask what to do.
|
192
|
+
# Defaults to :revert. Overridden in the config.
|
193
|
+
|
194
|
+
def self.sigint_behavior
|
195
|
+
@config['sigint_behavior']
|
196
|
+
end
|
197
|
+
|
198
|
+
|
158
199
|
##
|
159
200
|
# How long to wait on a command to finish when no output is received.
|
160
201
|
# Defaults to 300 (seconds). Overridden in the config.
|
@@ -186,42 +227,6 @@ module Sunshine
|
|
186
227
|
end
|
187
228
|
|
188
229
|
|
189
|
-
##
|
190
|
-
# Adds an INT signal trap with its description on the stack.
|
191
|
-
# Returns a trap_item Array.
|
192
|
-
|
193
|
-
def self.add_trap desc, &block
|
194
|
-
trap_item = [desc, block]
|
195
|
-
(@trap_stack ||= []).unshift trap_item
|
196
|
-
trap_item
|
197
|
-
end
|
198
|
-
|
199
|
-
add_trap "Disconnecting all remote shells." do
|
200
|
-
RemoteShell.disconnect_all
|
201
|
-
end
|
202
|
-
|
203
|
-
|
204
|
-
##
|
205
|
-
# Call a trap item and display it's message.
|
206
|
-
|
207
|
-
def self.call_trap trap_item
|
208
|
-
return unless trap_item
|
209
|
-
|
210
|
-
msg, block = trap_item
|
211
|
-
|
212
|
-
logger.info :INT, msg do
|
213
|
-
block.call
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
|
218
|
-
##
|
219
|
-
# Remove a trap_item from the stack.
|
220
|
-
|
221
|
-
def self.delete_trap trap_item
|
222
|
-
@trap_stack.delete trap_item
|
223
|
-
end
|
224
|
-
|
225
230
|
|
226
231
|
##
|
227
232
|
# Global value of sudo to use. Returns true, nil, or a username.
|
@@ -264,6 +269,15 @@ module Sunshine
|
|
264
269
|
end
|
265
270
|
|
266
271
|
load_config_file USER_CONFIG_FILE
|
272
|
+
|
273
|
+
@config['deploy_env'] =
|
274
|
+
ENV['DEPLOY_ENV'] ||
|
275
|
+
ENV['env'] ||
|
276
|
+
ENV['RACK_ENV'] ||
|
277
|
+
ENV['RAILS_ENV'] ||
|
278
|
+
@config['deploy_env']
|
279
|
+
|
280
|
+
@config
|
267
281
|
end
|
268
282
|
|
269
283
|
|
@@ -282,15 +296,18 @@ module Sunshine
|
|
282
296
|
def self.setup new_config={}, reset=false
|
283
297
|
@config = DEFAULT_CONFIG.dup if reset
|
284
298
|
|
285
|
-
|
299
|
+
TrapStack.trap_signal :INT do |msg|
|
286
300
|
$stderr << "\n\n"
|
287
301
|
logger.indent = 0
|
288
302
|
logger.fatal :INT, "Caught INT signal!"
|
303
|
+
logger.info :INT, msg
|
304
|
+
end
|
289
305
|
|
290
|
-
|
291
|
-
|
306
|
+
TrapStack.add_trap "Disconnecting all remote shells." do
|
307
|
+
RemoteShell.disconnect_all
|
292
308
|
end
|
293
309
|
|
310
|
+
|
294
311
|
require_libs(*new_config['require'])
|
295
312
|
|
296
313
|
config.merge! new_config
|
@@ -369,6 +386,7 @@ module Sunshine
|
|
369
386
|
|
370
387
|
|
371
388
|
require 'sunshine/exceptions'
|
389
|
+
require 'sunshine/trap_stack'
|
372
390
|
|
373
391
|
require 'sunshine/shell'
|
374
392
|
require 'sunshine/remote_shell'
|
data/lib/sunshine/app.rb
CHANGED
@@ -176,6 +176,8 @@ module Sunshine
|
|
176
176
|
|
177
177
|
@deploy_name = options[:deploy_name] || Time.now.to_i.to_s
|
178
178
|
|
179
|
+
@deploy_env = options[:deploy_env] if options[:deploy_env]
|
180
|
+
|
179
181
|
set_deploy_paths options[:root_path]
|
180
182
|
|
181
183
|
@server_apps = server_apps_from_config options[:remote_shells]
|
@@ -192,7 +194,7 @@ module Sunshine
|
|
192
194
|
|
193
195
|
@post_user_lambdas = []
|
194
196
|
|
195
|
-
@
|
197
|
+
@on_sigint = @on_exception = nil
|
196
198
|
end
|
197
199
|
|
198
200
|
|
@@ -220,7 +222,8 @@ module Sunshine
|
|
220
222
|
|
221
223
|
|
222
224
|
##
|
223
|
-
# Check if server apps are connected
|
225
|
+
# Check if all server apps are connected and returns a boolean.
|
226
|
+
# Supports any App#find options.
|
224
227
|
|
225
228
|
def connected? options=nil
|
226
229
|
each options do |server_app|
|
@@ -231,6 +234,19 @@ module Sunshine
|
|
231
234
|
end
|
232
235
|
|
233
236
|
|
237
|
+
##
|
238
|
+
# Check if any server apps are connected and returns a boolean.
|
239
|
+
# Supports any App#find options.
|
240
|
+
|
241
|
+
def any_connected? options=nil
|
242
|
+
each options do |server_app|
|
243
|
+
return true if server_app.shell.connected?
|
244
|
+
end
|
245
|
+
|
246
|
+
false
|
247
|
+
end
|
248
|
+
|
249
|
+
|
234
250
|
##
|
235
251
|
# Disconnect server apps. Supports any App#find options.
|
236
252
|
|
@@ -247,6 +263,13 @@ module Sunshine
|
|
247
263
|
# Deploy the application to deploy servers and
|
248
264
|
# call user's post-deploy code. Supports any App#find options.
|
249
265
|
#
|
266
|
+
# If the deploy fails or an exception is raised, it will attempt to
|
267
|
+
# run the Sunshine.failed_deploy_behavior, which is set to :revert by
|
268
|
+
# default. However, this is not true of ssh connection failures.
|
269
|
+
#
|
270
|
+
# If the deploy is interrupted by a SIGINT, it will attempt to run
|
271
|
+
# the Sunshine.sigint_behavior, which is set to :revert by default.
|
272
|
+
#
|
250
273
|
# Note: The deploy method will stop the former deploy just before
|
251
274
|
# symlink and the passed block is run.
|
252
275
|
#
|
@@ -254,28 +277,28 @@ module Sunshine
|
|
254
277
|
# run App#start.
|
255
278
|
|
256
279
|
def deploy options=nil
|
257
|
-
success = false
|
258
|
-
prev_connection = connected?
|
259
280
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
281
|
+
state = {
|
282
|
+
:success => false,
|
283
|
+
:stopped => false,
|
284
|
+
:symlinked => false
|
285
|
+
}
|
265
286
|
|
266
|
-
Sunshine.logger.info :app, "Beginning
|
287
|
+
Sunshine.logger.info :app, "Beginning #{@name} deploy"
|
267
288
|
|
268
|
-
with_session options do
|
289
|
+
with_session options do |app|
|
290
|
+
|
291
|
+
interruptable state do
|
292
|
+
raise DeployError, "No servers defined for #{@name}" if
|
293
|
+
@server_apps.empty?
|
269
294
|
|
270
|
-
with_filter options do |app|
|
271
295
|
make_app_directories
|
272
296
|
checkout_codebase
|
273
297
|
|
274
|
-
stop
|
275
|
-
|
276
|
-
symlink_current_dir
|
298
|
+
state[:stopped] = true if stop
|
299
|
+
state[:symlinked] = true if symlink_current_dir
|
277
300
|
|
278
|
-
yield
|
301
|
+
yield self if block_given?
|
279
302
|
|
280
303
|
run_post_user_lambdas
|
281
304
|
|
@@ -287,29 +310,136 @@ module Sunshine
|
|
287
310
|
|
288
311
|
register_as_deployed
|
289
312
|
|
290
|
-
success = start :force => true
|
291
|
-
|
292
|
-
remove_old_deploys
|
293
|
-
success &&= deployed?
|
313
|
+
state[:success] = true if start! :force => true
|
294
314
|
end
|
315
|
+
|
316
|
+
remove_old_deploys if state[:success] rescue
|
317
|
+
Sunshine.logger.error :app, "Could not remove old deploys"
|
318
|
+
|
319
|
+
state[:success] &&= deployed?
|
295
320
|
end
|
296
321
|
|
297
|
-
Sunshine.logger.info :app, "
|
322
|
+
Sunshine.logger.info :app, "Finished #{@name} deploy" if state[:success]
|
323
|
+
state[:success]
|
324
|
+
end
|
298
325
|
|
299
|
-
rescue => e
|
300
|
-
message = "#{e.class}: #{e.message}"
|
301
326
|
|
302
|
-
|
327
|
+
##
|
328
|
+
# Handles SIGINTs and exceptions according to rules set by
|
329
|
+
# Sunshine.sigint_behavior and Sunshine.exception_behavior
|
330
|
+
# or with the override hooks App#on_sigint and App#on_exception.
|
331
|
+
|
332
|
+
def interruptable options={}
|
333
|
+
interrupt_trap =
|
334
|
+
TrapStack.add_trap "Interrupted #{@name}" do
|
335
|
+
handle_sigint options
|
336
|
+
end
|
337
|
+
|
338
|
+
yield if block_given?
|
339
|
+
|
340
|
+
rescue => e
|
341
|
+
Sunshine.logger.error :app, "#{e.class}: #{e.message}" do
|
303
342
|
Sunshine.logger.error '>>', e.backtrace.join("\n")
|
304
|
-
revert! options
|
305
|
-
start options
|
306
343
|
end
|
307
344
|
|
345
|
+
handle_exception e, options
|
346
|
+
|
308
347
|
ensure
|
309
|
-
|
310
|
-
|
348
|
+
TrapStack.delete_trap interrupt_trap
|
349
|
+
end
|
350
|
+
|
351
|
+
|
352
|
+
##
|
353
|
+
# Calls the Apps on_sigint hook or the default Sunshine.sigint_behavior.
|
311
354
|
|
312
|
-
|
355
|
+
def handle_sigint state={}
|
356
|
+
return @on_sigint.call(state) if @on_sigint
|
357
|
+
handle_interruption Sunshine.sigint_behavior, state
|
358
|
+
end
|
359
|
+
|
360
|
+
|
361
|
+
##
|
362
|
+
# Calls the Apps on_exception hook or the default
|
363
|
+
# Sunshine.exception_behavior.
|
364
|
+
|
365
|
+
def handle_exception exception, state={}
|
366
|
+
return @on_exception.call(exception, state) if @on_exception
|
367
|
+
handle_interruption Sunshine.exception_behavior, state
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
##
|
372
|
+
# Set this to define the behavior of SIGINT during a deploy.
|
373
|
+
# Defines what to do when an INT signal is received when running
|
374
|
+
# a proc through App#interruptable. Used primarily to catch SIGINTs
|
375
|
+
# during deploys. Passes the block a hash with the state of the deploy:
|
376
|
+
#
|
377
|
+
# app.on_sigint do |deploy_state_hash|
|
378
|
+
# deploy_state_hash
|
379
|
+
# #=> {:stopped => true, :symlinked => true, :success => false}
|
380
|
+
# end
|
381
|
+
|
382
|
+
def on_sigint &block
|
383
|
+
@on_sigint = block
|
384
|
+
end
|
385
|
+
|
386
|
+
|
387
|
+
##
|
388
|
+
# Set this to define the behavior of exceptions during a deploy.
|
389
|
+
# Defines what to do when an exception is received when running
|
390
|
+
# a proc through App#interruptable. Used primarily to catch exceptions
|
391
|
+
# during deploys. Passes the block the exception and a hash with the
|
392
|
+
# state of the deploy:
|
393
|
+
#
|
394
|
+
# app.on_exception do |exception, deploy_state_hash|
|
395
|
+
# # do something...
|
396
|
+
# end
|
397
|
+
|
398
|
+
def on_exception &block
|
399
|
+
@on_exception = block
|
400
|
+
end
|
401
|
+
|
402
|
+
|
403
|
+
##
|
404
|
+
# Handles the behavior of a failed or interrupted deploy.
|
405
|
+
# Takes a behavior symbol defining how to handle the interruption
|
406
|
+
# and a hash representing the state of the deploy when it was
|
407
|
+
# interrupted.
|
408
|
+
#
|
409
|
+
# Supported bahavior symbols are:
|
410
|
+
# ::revert: Revert to previous deploy (default)
|
411
|
+
# ::console: Start an interactive console with the app's binding
|
412
|
+
# ::exit: Stop deploy and exit
|
413
|
+
# ::prompt: Ask what to do
|
414
|
+
#
|
415
|
+
# The state hash supports the following keys:
|
416
|
+
# ::stopped: Was the previous deploy stopped.
|
417
|
+
# ::symlinked: Was the new deployed symlinked as the current deploy.
|
418
|
+
|
419
|
+
def handle_interruption behavior, state={}
|
420
|
+
case behavior
|
421
|
+
|
422
|
+
when :exit
|
423
|
+
Sunshine.exit 1, "Error: Deploy of #{@name} failed"
|
424
|
+
|
425
|
+
when :revert
|
426
|
+
revert! if state[:symlinked]
|
427
|
+
start if state[:stopped]
|
428
|
+
|
429
|
+
when Sunshine.interactive? && :console
|
430
|
+
self.console!
|
431
|
+
|
432
|
+
when Sunshine.interactive? && :prompt
|
433
|
+
Sunshine.shell.choose do |menu|
|
434
|
+
menu.prompt = "Deploy interrupted:"
|
435
|
+
menu.choice(:revert) { handle_interruption :revert, state }
|
436
|
+
menu.choice(:console){ handle_interruption :console, state }
|
437
|
+
menu.choice(:exit) { handle_interruption :exit, state }
|
438
|
+
end
|
439
|
+
|
440
|
+
else
|
441
|
+
raise DeployError, "Deploy of #{@name} was interrupted."
|
442
|
+
end
|
313
443
|
end
|
314
444
|
|
315
445
|
|
@@ -343,7 +473,7 @@ module Sunshine
|
|
343
473
|
# application and job name, including previous deploys.
|
344
474
|
|
345
475
|
def add_to_crontab name, cronjob, options=nil
|
346
|
-
|
476
|
+
each options do |server_app|
|
347
477
|
server_app.crontab[name] << cronjob
|
348
478
|
end
|
349
479
|
end
|
@@ -357,7 +487,7 @@ module Sunshine
|
|
357
487
|
# application and job name, including previous deploys.
|
358
488
|
|
359
489
|
def cronjob name, cronjob, options=nil
|
360
|
-
|
490
|
+
each options do |server_app|
|
361
491
|
server_app.crontab[name] = cronjob
|
362
492
|
end
|
363
493
|
end
|
@@ -369,7 +499,7 @@ module Sunshine
|
|
369
499
|
# add_to_script :start, "start_mail", :role => :mail
|
370
500
|
|
371
501
|
def add_to_script name, script, options=nil
|
372
|
-
|
502
|
+
each options do |server_app|
|
373
503
|
server_app.scripts[name] << script
|
374
504
|
end
|
375
505
|
end
|
@@ -432,13 +562,44 @@ module Sunshine
|
|
432
562
|
end
|
433
563
|
|
434
564
|
|
565
|
+
##
|
566
|
+
# Starts an IRB console with the instance's binding.
|
567
|
+
|
568
|
+
def console!
|
569
|
+
IRB.setup nil unless defined?(IRB::UnrecognizedSwitch)
|
570
|
+
|
571
|
+
workspace = IRB::WorkSpace.new binding
|
572
|
+
irb = IRB::Irb.new workspace
|
573
|
+
|
574
|
+
irb.context.irb_name = "sunshine(#{@name})"
|
575
|
+
irb.context.prompt_c = "%N:%03n:%i* "
|
576
|
+
irb.context.prompt_i = "%N:%03n:%i> "
|
577
|
+
irb.context.prompt_n = "%N:%03n:%i> "
|
578
|
+
|
579
|
+
IRB.class_eval do
|
580
|
+
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
581
|
+
@CONF[:MAIN_CONTEXT] = irb.context
|
582
|
+
end
|
583
|
+
|
584
|
+
#TODO: remove sigint trap when irb session is closed
|
585
|
+
#trap("INT") do
|
586
|
+
# irb.signal_handle
|
587
|
+
#end
|
588
|
+
|
589
|
+
catch(:IRB_EXIT) do
|
590
|
+
irb.eval_input
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
|
435
595
|
##
|
436
596
|
# Checks out the app's codebase to one or all deploy servers.
|
437
597
|
# Supports all App#find options, plus:
|
438
598
|
# :copy:: Bool - Checkout locally and rsync; defaults to false.
|
439
599
|
|
440
600
|
def checkout_codebase options=nil
|
441
|
-
copy_option = options
|
601
|
+
copy_option = options[:copy] if options
|
602
|
+
exclude = options.delete(:exclude) if options
|
442
603
|
|
443
604
|
if @remote_checkout && !copy_option
|
444
605
|
with_server_apps options,
|
@@ -451,13 +612,13 @@ module Sunshine
|
|
451
612
|
tmp_path = File.join Sunshine::TMP_DIR, "#{@name}_checkout"
|
452
613
|
scm_info = @repo.checkout_to tmp_path
|
453
614
|
|
615
|
+
scm_info[:exclude] =
|
616
|
+
[Sunshine.exclude_paths, exclude].flatten.compact
|
617
|
+
|
454
618
|
with_server_apps options,
|
455
619
|
:send => [:upload_codebase, tmp_path, scm_info]
|
456
620
|
end
|
457
621
|
end
|
458
|
-
|
459
|
-
rescue => e
|
460
|
-
raise CriticalDeployError, e
|
461
622
|
end
|
462
623
|
|
463
624
|
|
@@ -612,9 +773,6 @@ module Sunshine
|
|
612
773
|
with_server_apps options,
|
613
774
|
:msg => "Creating #{@name} directories",
|
614
775
|
:send => :make_app_directories
|
615
|
-
|
616
|
-
rescue => e
|
617
|
-
raise FatalDeployError, e
|
618
776
|
end
|
619
777
|
|
620
778
|
|
@@ -692,6 +850,18 @@ module Sunshine
|
|
692
850
|
end
|
693
851
|
|
694
852
|
|
853
|
+
##
|
854
|
+
# Run the restart script of a deployed app on the specified
|
855
|
+
# deploy servers. Raises an exception on failure.
|
856
|
+
# Post-deploy only.
|
857
|
+
|
858
|
+
def restart! options=nil
|
859
|
+
with_server_apps options,
|
860
|
+
:msg => "Running restart script",
|
861
|
+
:send => :restart!
|
862
|
+
end
|
863
|
+
|
864
|
+
|
695
865
|
##
|
696
866
|
# Runs bundler on deploy servers.
|
697
867
|
|
@@ -699,9 +869,6 @@ module Sunshine
|
|
699
869
|
with_server_apps options,
|
700
870
|
:msg => "Running Bundler",
|
701
871
|
:send => [:run_bundler, options]
|
702
|
-
|
703
|
-
rescue => e
|
704
|
-
raise CriticalDeployError, e
|
705
872
|
end
|
706
873
|
|
707
874
|
|
@@ -712,9 +879,6 @@ module Sunshine
|
|
712
879
|
with_server_apps options,
|
713
880
|
:msg => "Running GemInstaller",
|
714
881
|
:send => [:run_geminstaller, options]
|
715
|
-
|
716
|
-
rescue => e
|
717
|
-
raise CriticalDeployError, e
|
718
882
|
end
|
719
883
|
|
720
884
|
|
@@ -741,6 +905,18 @@ module Sunshine
|
|
741
905
|
end
|
742
906
|
|
743
907
|
|
908
|
+
##
|
909
|
+
# Run the given script of a deployed app on the specified
|
910
|
+
# deploy servers. Raises an exception on failure.
|
911
|
+
# Post-deploy only.
|
912
|
+
|
913
|
+
def run_script! name, options=nil
|
914
|
+
with_server_apps options,
|
915
|
+
:msg => "Running #{name} script",
|
916
|
+
:send => [:run_script!, name, options]
|
917
|
+
end
|
918
|
+
|
919
|
+
|
744
920
|
##
|
745
921
|
# Run a sass task on any or all deploy servers.
|
746
922
|
|
@@ -763,6 +939,8 @@ module Sunshine
|
|
763
939
|
@shell_env.merge!(env_hash)
|
764
940
|
|
765
941
|
with_server_apps :all,
|
942
|
+
:no_threads => true,
|
943
|
+
:no_session => true,
|
766
944
|
:msg => "Shell env: #{@shell_env.inspect}" do |server_app|
|
767
945
|
server_app.shell_env.merge!(@shell_env)
|
768
946
|
end
|
@@ -783,6 +961,18 @@ module Sunshine
|
|
783
961
|
end
|
784
962
|
|
785
963
|
|
964
|
+
##
|
965
|
+
# Run the start script of a deployed app on the specified
|
966
|
+
# deploy servers. Raises an exception on failure.
|
967
|
+
# Post-deploy only.
|
968
|
+
|
969
|
+
def start! options=nil
|
970
|
+
with_server_apps options,
|
971
|
+
:msg => "Running start script",
|
972
|
+
:send => [:start!, options]
|
973
|
+
end
|
974
|
+
|
975
|
+
|
786
976
|
##
|
787
977
|
# Get a hash of which deploy server apps are :running or :down.
|
788
978
|
# Post-deploy only.
|
@@ -810,12 +1000,26 @@ module Sunshine
|
|
810
1000
|
end
|
811
1001
|
|
812
1002
|
|
1003
|
+
##
|
1004
|
+
# Run the stop script of a deployed app on the specified
|
1005
|
+
# deploy servers. Raises an exception on failure.
|
1006
|
+
# Post-deploy only.
|
1007
|
+
|
1008
|
+
def stop! options=nil
|
1009
|
+
with_server_apps options,
|
1010
|
+
:msg => "Running stop script",
|
1011
|
+
:send => :stop!
|
1012
|
+
end
|
1013
|
+
|
1014
|
+
|
813
1015
|
##
|
814
1016
|
# Use sudo on deploy servers. Set to true/false, or
|
815
1017
|
# a username to use 'sudo -u'.
|
816
1018
|
|
817
1019
|
def sudo=(value)
|
818
1020
|
with_server_apps :all,
|
1021
|
+
:no_threads => true,
|
1022
|
+
:no_session => true,
|
819
1023
|
:msg => "Using sudo = #{value.inspect}" do |server_app|
|
820
1024
|
server_app.shell.sudo = value
|
821
1025
|
end
|
@@ -831,9 +1035,6 @@ module Sunshine
|
|
831
1035
|
with_server_apps options,
|
832
1036
|
:msg => "Symlinking #{@checkout_path} -> #{@current_path}",
|
833
1037
|
:send => :symlink_current_dir
|
834
|
-
|
835
|
-
rescue => e
|
836
|
-
raise CriticalDeployError, e
|
837
1038
|
end
|
838
1039
|
|
839
1040
|
|
@@ -916,6 +1117,7 @@ module Sunshine
|
|
916
1117
|
# a session to avoid multiple ssh login prompts. Supports all App#find
|
917
1118
|
# options, plus:
|
918
1119
|
# :no_threads:: bool - disable threaded execution
|
1120
|
+
# :no_session:: bool - disable auto session creation
|
919
1121
|
# :msg:: "some message" - log message
|
920
1122
|
#
|
921
1123
|
# app.with_server_apps :all, :msg => "doing something" do |server_app|
|
@@ -925,12 +1127,16 @@ module Sunshine
|
|
925
1127
|
# app.with_server_apps :role => :db, :user => "bob" do |server_app|
|
926
1128
|
# # do something here
|
927
1129
|
# end
|
1130
|
+
#
|
1131
|
+
# Note: App#with_server_apps calls App#with_session. If you do not need
|
1132
|
+
# or want a server connection you can pass :no_session.
|
928
1133
|
|
929
1134
|
def with_server_apps search_options, options={}
|
930
1135
|
options = search_options.merge options if Hash === search_options
|
931
1136
|
|
932
1137
|
message = options[:msg]
|
933
1138
|
method = options[:no_threads] ? :each : :threaded_each
|
1139
|
+
auto_session = !options[:no_session]
|
934
1140
|
|
935
1141
|
block = lambda do
|
936
1142
|
send(method, search_options) do |server_app|
|
@@ -945,7 +1151,7 @@ module Sunshine
|
|
945
1151
|
end
|
946
1152
|
|
947
1153
|
|
948
|
-
|
1154
|
+
msg_block = lambda do
|
949
1155
|
if message
|
950
1156
|
Sunshine.logger.info(:app, message, &block)
|
951
1157
|
|
@@ -953,6 +1159,8 @@ module Sunshine
|
|
953
1159
|
block.call
|
954
1160
|
end
|
955
1161
|
end
|
1162
|
+
|
1163
|
+
auto_session ? with_session(&msg_block) : msg_block.call
|
956
1164
|
end
|
957
1165
|
|
958
1166
|
|
@@ -960,16 +1168,22 @@ module Sunshine
|
|
960
1168
|
# Runs block ensuring a connection to remote_shells.
|
961
1169
|
# Connecting and disconnecting will be ignored if a session
|
962
1170
|
# already exists. Supports all App#find options.
|
1171
|
+
#
|
1172
|
+
# Ensures that servers are disconnected after the block is run
|
1173
|
+
# if servers were not previously connected.
|
963
1174
|
|
964
1175
|
def with_session options=nil
|
1176
|
+
with_filter options do
|
1177
|
+
prev_connection = connected?
|
965
1178
|
|
966
|
-
|
967
|
-
|
1179
|
+
begin
|
1180
|
+
connect unless prev_connection
|
1181
|
+
yield self
|
968
1182
|
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
1183
|
+
ensure
|
1184
|
+
disconnect unless prev_connection
|
1185
|
+
end
|
1186
|
+
end
|
973
1187
|
end
|
974
1188
|
|
975
1189
|
|