sunshine 1.0.3 → 1.1.0

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.
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'