sunshine 1.0.3 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/History.txt +22 -2
  2. data/Manifest.txt +7 -0
  3. data/README.txt +333 -57
  4. data/Rakefile +1 -1
  5. data/lib/commands/add.rb +2 -2
  6. data/lib/commands/default.rb +15 -8
  7. data/lib/commands/list.rb +5 -3
  8. data/lib/commands/restart.rb +2 -2
  9. data/lib/commands/rm.rb +2 -2
  10. data/lib/commands/run.rb +2 -2
  11. data/lib/commands/start.rb +2 -2
  12. data/lib/commands/stop.rb +2 -2
  13. data/lib/sunshine.rb +117 -132
  14. data/lib/sunshine/app.rb +116 -10
  15. data/lib/sunshine/crontab.rb +11 -2
  16. data/lib/sunshine/daemon.rb +60 -46
  17. data/lib/sunshine/daemons/apache.rb +10 -2
  18. data/lib/sunshine/daemons/ar_sendmail.rb +0 -6
  19. data/lib/sunshine/daemons/delayed_job.rb +2 -0
  20. data/lib/sunshine/daemons/mongrel_rails.rb +32 -0
  21. data/lib/sunshine/daemons/nginx.rb +3 -0
  22. data/lib/sunshine/daemons/rainbows.rb +2 -0
  23. data/lib/sunshine/daemons/server.rb +51 -24
  24. data/lib/sunshine/daemons/server_cluster.rb +47 -0
  25. data/lib/sunshine/daemons/thin.rb +36 -0
  26. data/lib/sunshine/daemons/unicorn.rb +4 -1
  27. data/lib/sunshine/dependencies.rb +10 -3
  28. data/lib/sunshine/healthcheck.rb +2 -2
  29. data/lib/sunshine/remote_shell.rb +11 -2
  30. data/lib/sunshine/repo.rb +1 -1
  31. data/lib/sunshine/repos/rsync_repo.rb +1 -0
  32. data/templates/apache/apache.conf.erb +25 -18
  33. data/templates/mongrel_rails/mongrel_rails.conf.erb +9 -0
  34. data/templates/nginx/nginx.conf.erb +12 -9
  35. data/templates/thin/thin.conf.erb +12 -0
  36. data/test/helper_methods.rb +161 -0
  37. data/test/unit/test_daemon.rb +1 -8
  38. data/test/unit/test_nginx.rb +1 -1
  39. data/test/unit/test_server.rb +16 -0
  40. data/test/unit/test_server_cluster.rb +46 -0
  41. data/test/unit/test_sunshine.rb +18 -12
  42. metadata +14 -11
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ task :manifest do
8
8
  manifest_file = "Manifest.txt"
9
9
 
10
10
  gem_files = record_files do |f|
11
- next if f =~ /^(tmp|pkg|deploy_tests)/
11
+ next if f =~ /^(tmp|pkg|deploy_scripts)/
12
12
  puts(f)
13
13
  true
14
14
  end
data/lib/commands/add.rb CHANGED
@@ -3,7 +3,7 @@ module Sunshine
3
3
  ##
4
4
  # Registers a path as a sunshine application for control via sunshine.
5
5
  #
6
- # Usage: sunshine add app_path [more paths...] [options]
6
+ # Usage: sunshine add [options] app_path [more paths...]
7
7
  #
8
8
  # Arguments:
9
9
  # app_path Path to the application to add.
@@ -83,7 +83,7 @@ module Sunshine
83
83
  parse_remote_args(argv) do |opt, options|
84
84
  opt.banner = <<-EOF
85
85
 
86
- Usage: #{opt.program_name} add app_path [more paths...] [options]
86
+ Usage: #{opt.program_name} add [options] app_path [more paths...]
87
87
 
88
88
  Arguments:
89
89
  app_path Path to the application to add.
@@ -58,10 +58,17 @@ module Sunshine
58
58
 
59
59
  yield opt if block_given?
60
60
 
61
- opt.on('-S', '--sudo [USER]',
62
- 'Run remote commands using sudo or sudo -u USER.') do |value|
63
- options['sudo'] = value || true
64
- end if options
61
+ if options
62
+ opt.on('-R', '--require lib1,lib2', Array,
63
+ 'Require a library or gem.') do |value|
64
+ options['require'] = value
65
+ end
66
+
67
+ opt.on('-S', '--sudo [USER]',
68
+ 'Run remote commands using sudo or sudo -u USER.') do |value|
69
+ options['sudo'] = value || true
70
+ end
71
+ end
65
72
  end
66
73
  end
67
74
 
@@ -80,13 +87,13 @@ Sunshine is an object oriented deploy tool for rack applications.
80
87
  Usage:
81
88
  #{opt.program_name} -h/--help
82
89
  #{opt.program_name} -v/--version
83
- #{opt.program_name} command [arguments...] [options...]
90
+ #{opt.program_name} command [options...] [arguments...]
84
91
 
85
92
  Examples:
86
93
  #{opt.program_name} deploy deploy_script.rb
87
- #{opt.program_name} restart myapp -r user@server.com,user@host.com
88
- #{opt.program_name} list myapp myotherapp --health -r user@server.com
89
- #{opt.program_name} list myapp --status
94
+ #{opt.program_name} restart -r user@server.com,user@host.com myapp
95
+ #{opt.program_name} list --health -r user@server.com myapp myotherapp
96
+ #{opt.program_name} list --status myapp
90
97
 
91
98
  Commands:
92
99
  add Register an app with #{opt.program_name}
data/lib/commands/list.rb CHANGED
@@ -3,7 +3,7 @@ module Sunshine
3
3
  ##
4
4
  # List and perform simple state actions on lists of sunshine apps.
5
5
  #
6
- # Usage: sunshine list app_name [more names...] [options]
6
+ # Usage: sunshine list [options] app_name [more names...]
7
7
  #
8
8
  # Arguments:
9
9
  # app_name Name of an application to list.
@@ -268,7 +268,9 @@ module Sunshine
268
268
  # Load the app list yaml file from the server.
269
269
 
270
270
  def self.load_list server
271
- list = YAML.load(server.call(Sunshine::READ_LIST_CMD))
271
+ yml_list = server.call "cat #{Sunshine::APP_LIST_PATH} || echo ''"
272
+
273
+ list = YAML.load yml_list
272
274
  list = {} unless Hash === list
273
275
  list
274
276
  end
@@ -290,7 +292,7 @@ module Sunshine
290
292
  parse_remote_args(argv) do |opt, options|
291
293
  opt.banner = <<-EOF
292
294
 
293
- Usage: #{opt.program_name} list app_name [more names...] [options]
295
+ Usage: #{opt.program_name} list [options] app_name [more names...]
294
296
 
295
297
  Arguments:
296
298
  app_name Name of an application to list.
@@ -3,7 +3,7 @@ module Sunshine
3
3
  ##
4
4
  # Runs the restart script of all specified sunshine apps.
5
5
  #
6
- # Usage: sunshine restart app_name [more names...] [options]
6
+ # Usage: sunshine restart [options] app_name [more names...]
7
7
  #
8
8
  # Arguments:
9
9
  # app_name Name of the application to restart.
@@ -49,7 +49,7 @@ module Sunshine
49
49
  parse_remote_args(argv) do |opt, options|
50
50
  opt.banner = <<-EOF
51
51
 
52
- Usage: #{opt.program_name} restart app_name [more names...] [options]
52
+ Usage: #{opt.program_name} restart [options] app_name [more names...]
53
53
 
54
54
  Arguments:
55
55
  app_name Name of the application to restart.
data/lib/commands/rm.rb CHANGED
@@ -3,7 +3,7 @@ module Sunshine
3
3
  ##
4
4
  # Unregister a sunshine app.
5
5
  #
6
- # Usage: sunshine rm app_name [more names...] [options]
6
+ # Usage: sunshine rm [options] app_name [more names...]
7
7
  #
8
8
  # Arguments:
9
9
  # app_name Name of the application to remove.
@@ -67,7 +67,7 @@ module Sunshine
67
67
  parse_remote_args(argv) do |opt, options|
68
68
  opt.banner = <<-EOF
69
69
 
70
- Usage: #{opt.program_name} rm app_name [more names...] [options]
70
+ Usage: #{opt.program_name} rm [options] app_name [more names...]
71
71
 
72
72
  Arguments:
73
73
  app_name Name of the application to remove.
data/lib/commands/run.rb CHANGED
@@ -3,7 +3,7 @@ module Sunshine
3
3
  ##
4
4
  # Run one or more sunshine scripts.
5
5
  #
6
- # Usage: sunshine run [run_file] [options]
6
+ # Usage: sunshine run [options] [run_file] ...
7
7
  #
8
8
  # Arguments:
9
9
  # run_file Load a script or app path. Defaults to ./Sunshine
@@ -113,7 +113,7 @@ module Sunshine
113
113
  opts = opt_parser(options) do |opt|
114
114
  opt.banner = <<-EOF
115
115
 
116
- Usage: #{opt.program_name} run [run_file] [options]
116
+ Usage: #{opt.program_name} run [options] [run_file] ...
117
117
 
118
118
  Arguments:
119
119
  run_file Load a script or app path. Defaults to ./Sunshine
@@ -3,7 +3,7 @@ module Sunshine
3
3
  ##
4
4
  # Runs the start script of all specified sunshine apps.
5
5
  #
6
- # Usage: sunshine start app_name [more names...] [options]
6
+ # Usage: sunshine start [options] app_name [more names...]
7
7
  #
8
8
  # Arguments:
9
9
  # app_name Name of the application to start.
@@ -55,7 +55,7 @@ module Sunshine
55
55
  parse_remote_args(argv) do |opt, options|
56
56
  opt.banner = <<-EOF
57
57
 
58
- Usage: #{opt.program_name} start app_name [more names...] [options]
58
+ Usage: #{opt.program_name} start [options] app_name [more names...]
59
59
 
60
60
  Arguments:
61
61
  app_name Name of the application to start.
data/lib/commands/stop.rb CHANGED
@@ -3,7 +3,7 @@ module Sunshine
3
3
  ##
4
4
  # Runs the stop script of all specified sunshine apps.
5
5
  #
6
- # Usage: sunshine stop app_name [more names...] [options]
6
+ # Usage: sunshine stop [options] app_name [more names...]
7
7
  #
8
8
  # Arguments:
9
9
  # app_name Name of the application to stop.
@@ -48,7 +48,7 @@ module Sunshine
48
48
  parse_remote_args(argv) do |opt, options|
49
49
  opt.banner = <<-EOF
50
50
 
51
- Usage: #{opt.program_name} stop app_name [more names...] [options]
51
+ Usage: #{opt.program_name} stop [options] app_name [more names...]
52
52
 
53
53
  Arguments:
54
54
  app_name Name of the application to stop.
data/lib/sunshine.rb CHANGED
@@ -4,7 +4,6 @@ require 'rainbow'
4
4
  require 'highline'
5
5
  require 'json'
6
6
 
7
-
8
7
  require 'yaml'
9
8
  require 'erb'
10
9
  require 'logger'
@@ -14,76 +13,64 @@ require 'fileutils'
14
13
  require 'tmpdir'
15
14
 
16
15
  ##
17
- # Sunshine is an object oriented deploy tool for rack applications.
18
- #
19
- # Writing a Sunshine config script is easy:
20
- #
21
- # options = {
22
- # :name => 'myapp',
23
- # :repo => {:type => :svn, :url => 'svn://blah...'},
24
- # :deploy_path => '/usr/local/myapp',
25
- # :remote_shells => ['user@someserver.com']
26
- # }
27
- #
28
- # Sunshine::App.deploy(options) do |app|
29
- #
30
- # app.yum_install 'sqlite'
31
- # app.gem_install 'sqlite3-ruby'
32
- #
33
- # app.rake "db:migrate"
34
- #
35
- # app_server = Sunshine::Rainbows.new(app)
36
- # app_server.restart
37
- #
38
- # Sunshine::Nginx.new(app, :point_to => app_server).restart
39
- #
40
- # end
41
- #
42
- # The App::deploy and App::new methods also support passing
43
- # a path to a yaml file:
44
- #
45
- # app = Sunshine::App.new("path/to/config.yml")
46
- # app.deploy{|app| Sunshine::Rainbows.new(app).restart }
47
- #
48
- #
49
- # Command line execution:
50
- #
51
- # Usage:
52
- # sunshine -h/--help
53
- # sunshine -v/--version
54
- # sunshine command [arguments...] [options...]
55
- #
56
- # Examples:
57
- # sunshine run deploy_script.rb
58
- # sunshine restart myapp -r user@server.com,user@host.com
59
- # sunshine list myapp myotherapp --health -r user@server.com
60
- # sunshine list myapp --status
61
- #
62
- # Commands:
63
- # add Register an app with sunshine
64
- # list Display deployed apps
65
- # restart Restart a deployed app
66
- # rm Unregister an app with sunshine
67
- # run Run a Sunshine script
68
- # start Start a deployed app
69
- # stop Stop a deployed app
70
- #
71
- # For more help on sunshine commands, use 'sunshine COMMAND --help'
16
+ # Main module, used for configuration and running commands.
72
17
 
73
18
  module Sunshine
74
19
 
75
20
  ##
76
21
  # Sunshine version.
77
- VERSION = '1.0.3'
22
+ VERSION = '1.1.0'
78
23
 
24
+ ##
25
+ # Path to the list of installed sunshine apps.
26
+ APP_LIST_PATH = "~/.sunshine_list"
79
27
 
80
28
  ##
81
- # Handles input/output to the shell. See Sunshine::Shell.
29
+ # Commands supported by Sunshine.
30
+ COMMANDS = %w{add list restart rm run start stop}
82
31
 
83
- def self.shell
84
- @shell ||= Sunshine::Shell.new
32
+ ##
33
+ # File DATA from Sunshine run files.
34
+ DATA = defined?(::DATA) ? ::DATA : nil
35
+
36
+ ##
37
+ # Default configuration.
38
+ DEFAULT_CONFIG = {
39
+ 'level' => 'info',
40
+ 'deploy_env' => :development,
41
+ 'auto' => false,
42
+ 'max_deploy_versions' => 5,
43
+ 'web_directory' => '/var/www',
44
+ 'auto_dependencies' => true
45
+ }
46
+
47
+ ##
48
+ # Path where Sunshine assumes repo information can be found if missing.
49
+ PATH = Dir.getwd
50
+
51
+ ##
52
+ # Root directory of the Sunshine gem.
53
+ ROOT = File.expand_path File.join(File.dirname(__FILE__), "..")
54
+
55
+ ##
56
+ # Default Sunshine config file
57
+ USER_CONFIG_FILE = File.expand_path("~/.sunshine")
58
+
59
+ ##
60
+ # Temp directory used by various sunshine classes
61
+ # for uploads, checkouts, etc...
62
+ TMP_DIR = File.join Dir.tmpdir, "sunshine_#{$$}"
63
+ FileUtils.mkdir_p TMP_DIR
64
+
65
+
66
+ ##
67
+ # Returns the Sunshine config hash.
68
+
69
+ def self.config
70
+ @config ||= DEFAULT_CONFIG.dup
85
71
  end
86
72
 
73
+
87
74
  ##
88
75
  # The default deploy environment to use. Set with the -e option.
89
76
  # See App#deploy_env for app specific deploy environments.
@@ -109,6 +96,14 @@ module Sunshine
109
96
  end
110
97
 
111
98
 
99
+ ##
100
+ # Handles input/output to the shell. See Sunshine::Shell.
101
+
102
+ def self.shell
103
+ @shell ||= Sunshine::Shell.new
104
+ end
105
+
106
+
112
107
  ##
113
108
  # The default directory where apps should be deployed to:
114
109
  # '/var/www' by default. Overridden in the ~/.sunshine config file
@@ -132,8 +127,7 @@ module Sunshine
132
127
  # Handles all output for sunshine. See Sunshine::Output.
133
128
 
134
129
  def self.logger
135
- @logger ||= Sunshine::Output.new \
136
- :level => Logger.const_get(@config['level'].upcase)
130
+ @logger
137
131
  end
138
132
 
139
133
 
@@ -194,15 +188,6 @@ module Sunshine
194
188
  end
195
189
 
196
190
 
197
- trap "INT" do
198
- $stderr << "\n\n"
199
- logger.indent = 0
200
- logger.fatal :INT, "Caught INT signal!"
201
-
202
- call_trap @trap_stack.shift
203
- Kernel.exit 1
204
- end
205
-
206
191
 
207
192
  ##
208
193
  # Global value of sudo to use. Returns true, nil, or a username.
@@ -215,93 +200,87 @@ module Sunshine
215
200
 
216
201
 
217
202
  ##
218
- # Path to the list of installed sunshine apps.
219
- APP_LIST_PATH = "~/.sunshine_list"
203
+ # Cleanup after Sunshine has run, remove temp dirs, etc...
220
204
 
221
- READ_LIST_CMD = "test -f #{Sunshine::APP_LIST_PATH} && "+
222
- "cat #{APP_LIST_PATH} || echo ''"
205
+ def self.cleanup
206
+ FileUtils.rm_rf TMP_DIR if Dir.glob("#{TMP_DIR}/*").empty?
207
+ end
223
208
 
224
- ##
225
- # Commands supported by Sunshine
226
- COMMANDS = %w{add list restart rm run start stop}
227
209
 
228
210
  ##
229
- # Default Sunshine config file
230
- USER_CONFIG_FILE = File.expand_path("~/.sunshine")
211
+ # Loads a yaml config file to run setup with.
231
212
 
232
- ##
233
- # Default configuration
234
- DEFAULT_CONFIG = {
235
- 'level' => 'info',
236
- 'deploy_env' => :development,
237
- 'auto' => false,
238
- 'max_deploy_versions' => 5,
239
- 'web_directory' => '/var/www',
240
- 'auto_dependencies' => true
241
- }
213
+ def self.load_config_file conf
214
+ setup YAML.load_file(conf)
215
+ end
242
216
 
243
- ##
244
- # Temp directory used by various sunshine classes
245
- # for uploads, checkouts, etc...
246
- TMP_DIR = File.join Dir.tmpdir, "sunshine_#{$$}"
247
- FileUtils.mkdir_p TMP_DIR
248
217
 
249
218
  ##
250
- # Path where sunshine assumes repo information can be found if missing.
251
- PATH = Dir.getwd
219
+ # Loads the USER_CONFIG_FILE and runs setup. Creates the default
220
+ # config file and exits if not present.
252
221
 
253
- ##
254
- # File DATA from sunshine run files.
255
- DATA = defined?(::DATA) ? ::DATA : nil
222
+ def self.load_user_config
223
+ unless File.file? USER_CONFIG_FILE
224
+ File.open(USER_CONFIG_FILE, "w+"){|f| f.write DEFAULT_CONFIG.to_yaml}
225
+
226
+ msg = "Missing config file was created for you: #{USER_CONFIG_FILE}\n\n"
227
+ msg << DEFAULT_CONFIG.to_yaml
228
+
229
+ self.exit 1, msg
230
+ end
231
+
232
+ load_config_file USER_CONFIG_FILE
233
+ end
256
234
 
257
- ##
258
- # Root directory of the Sunshine gem.
259
- ROOT = File.expand_path File.join(File.dirname(__FILE__), "..")
260
235
 
261
236
  ##
262
- # Cleanup after sunshine has run, remove temp dirs, etc...
237
+ # Loads an array of libraries or gems.
263
238
 
264
- def self.cleanup
265
- FileUtils.rm_rf TMP_DIR if Dir.glob("#{TMP_DIR}/*").empty?
239
+ def self.require_libs(*libs)
240
+ libs.compact.each{|lib| require lib }
266
241
  end
267
242
 
268
243
 
269
244
  ##
270
- # Setup sunshine with a custom config:
245
+ # Setup Sunshine with a custom config:
271
246
  # Sunshine.setup 'level' => 'debug', 'deploy_env' => :production
272
247
 
273
248
  def self.setup new_config={}, reset=false
274
- @config = DEFAULT_CONFIG.dup if !defined?(@config) || reset
275
- @config.merge! new_config
276
- @config
249
+ @config = DEFAULT_CONFIG.dup if reset
250
+
251
+ trap "INT" do
252
+ $stderr << "\n\n"
253
+ logger.indent = 0
254
+ logger.fatal :INT, "Caught INT signal!"
255
+
256
+ call_trap @trap_stack.shift
257
+ exit 1
258
+ end
259
+
260
+ require_libs(*new_config['require'])
261
+
262
+ config.merge! new_config
263
+
264
+ log_level = Logger.const_get config['level'].upcase rescue Logger::INFO
265
+ @logger = Sunshine::Output.new :level => log_level
266
+
267
+ config
277
268
  end
278
269
 
279
270
 
280
271
  ##
281
- # Run sunshine with the passed argv and exits with appropriate exitcode.
272
+ # Run Sunshine with the passed argv and exits with appropriate exitcode.
282
273
  # run %w{run my_script.rb -l debug}
283
274
  # run %w{list -d}
275
+ # run %w{--rakefile}
284
276
 
285
277
  def self.run argv=ARGV
286
- unless File.file? USER_CONFIG_FILE
287
- File.open(USER_CONFIG_FILE, "w+"){|f| f.write DEFAULT_CONFIG.to_yaml}
288
-
289
- msg = "Missing config file was created for you: #{USER_CONFIG_FILE}\n\n"
290
- msg << DEFAULT_CONFIG.to_yaml
291
-
292
- self.exit 1, msg
293
- end
278
+ command = find_command argv.first
279
+ argv.shift if command
294
280
 
295
- command_name = find_command argv.first
296
- argv.shift if command_name
297
- command_name ||= "default"
281
+ command ||= DefaultCommand
298
282
 
299
- command = Sunshine.const_get("#{command_name.capitalize}Command")
300
-
301
- config = YAML.load_file USER_CONFIG_FILE
302
- config.merge! command.parse_args(argv)
303
-
304
- self.setup config, true
283
+ setup command.parse_args(argv)
305
284
 
306
285
  result = command.exec argv, config
307
286
 
@@ -312,12 +291,15 @@ module Sunshine
312
291
  ##
313
292
  # Find the sunshine command to run based on the passed name.
314
293
  # Handles partial command names if they can be uniquely mapped to a command.
315
- # find_command "dep" #=> "run"
316
- # find_command "zzz" #=> false
294
+ # find_command "ru" #=> Sunshine::RunCommand
295
+ # find_command "l" #=> Sunshine::ListCommand
296
+ # find_command "zzz" #=> nil
317
297
 
318
298
  def self.find_command name
319
299
  commands = COMMANDS.select{|c| c =~ /^#{name}/}
320
- commands.length == 1 && commands.first
300
+ return unless commands.length == 1 && commands.first
301
+
302
+ Sunshine.const_get "#{commands.first.capitalize}Command"
321
303
  end
322
304
 
323
305
 
@@ -371,11 +353,14 @@ module Sunshine
371
353
  require 'sunshine/repos/rsync_repo'
372
354
 
373
355
  require 'sunshine/daemon'
356
+ require 'sunshine/daemons/server_cluster'
374
357
  require 'sunshine/daemons/server'
375
358
  require 'sunshine/daemons/apache'
376
359
  require 'sunshine/daemons/nginx'
360
+ require 'sunshine/daemons/thin'
377
361
  require 'sunshine/daemons/unicorn'
378
362
  require 'sunshine/daemons/rainbows'
363
+ require 'sunshine/daemons/mongrel_rails'
379
364
  require 'sunshine/daemons/ar_sendmail'
380
365
  require 'sunshine/daemons/delayed_job'
381
366
 
@@ -398,6 +383,6 @@ module Sunshine
398
383
  require 'commands/stop'
399
384
  end
400
385
 
386
+ Sunshine.load_user_config
401
387
 
402
- Sunshine.setup
403
388
  require 'sunshine/dependencies'