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
@@ -8,6 +8,9 @@ module Sunshine
8
8
  def initialize app, options={}
9
9
  super
10
10
 
11
+ @supports_rack = false
12
+ @supports_passenger = true
13
+
11
14
  @dep_name = options[:dep_name] ||
12
15
  use_passenger? ? 'passenger-nginx' : 'nginx'
13
16
  end
@@ -2,6 +2,8 @@ module Sunshine
2
2
 
3
3
  ##
4
4
  # Simple server wrapper for Rainbows setup and control.
5
+ # Rainbows is strictly a backend server and therefore does not support
6
+ # the :point_to proxying option.
5
7
 
6
8
  class Rainbows < Unicorn
7
9
 
@@ -3,31 +3,48 @@ module Sunshine
3
3
  ##
4
4
  # An abstract class to wrap simple server software setup and start/stop.
5
5
  #
6
- # Child classes are expected to at least provide a start and stop bash script
7
- # by either overloading the start_cmd and stop_cmd methods, or by setting
8
- # @start_cmd and @stop_cmd. A restart_cmd method or @restart_cmd attribute
9
- # may also be specified if restart requires more functionality than simply
10
- # calling start_cmd && stop_cmd.
6
+ # Child classes are expected to at least provide a start_cmd bash script
7
+ # by either overloading the start_cmd method, or by setting @start_cmd.
8
+ # A restart_cmd and stop_cmd method or attribute may also be specified
9
+ # if restart requires more functionality than simply calling
10
+ # start_cmd && stop_cmd.
11
11
 
12
12
  class Server < Daemon
13
13
 
14
+ ##
15
+ # Creates a server cluster object:
16
+ # Mongrel.new_cluster 3, app, :port => 5000
17
+ # #=> [<# mongrel_5000 >, <# mongrel_5001 >, <# mongrel_5002 >]
18
+
19
+ def self.new_cluster count, app, options={}
20
+ ServerCluster.new self, count, app, options
21
+ end
22
+
23
+
14
24
  def self.binder_methods
15
- [:server_name, :port].concat super
25
+ [:server_name, :port, :target, :connections].concat super
16
26
  end
17
27
 
18
28
 
19
- attr_reader :server_name, :port
20
- attr_accessor :sigkill
29
+ attr_reader :server_name, :port, :target, :connections
30
+
31
+ attr_accessor :sigkill, :cluster
21
32
 
22
33
 
23
34
  # Server objects need only an App object to be instantiated.
24
35
  # All Daemon init options are supported plus the following:
25
36
  #
26
- # :port:: port_num - the port to run the server on
27
- # defaults to 80
37
+ # :connections:: num - Number of connections allowed per server;
38
+ # defaults to 1024.
39
+ #
40
+ # :point_to:: app|server - An app or server to point to,
41
+ # defaults to the passed app. If a server object is given, will
42
+ # act as a proxy. (Only valid on front-end servers - Nginx, Apache)
28
43
  #
29
- # :server_name:: myserver.com - host name used by server
30
- # defaults to nil
44
+ # :port:: port_num - The port to run the server on defaults to 80.
45
+ #
46
+ # :server_name:: myserver.com - Host name used by server
47
+ # defaults to the individual remote host.
31
48
  #
32
49
  # By default, servers also assign the option :role => :web.
33
50
 
@@ -36,20 +53,25 @@ module Sunshine
36
53
 
37
54
  super app, options
38
55
 
39
- @port = options[:port] || 80
40
- @sudo = options[:sudo] || @port < 1024 || nil
56
+ @connections = options[:connections] || 1024
57
+ @port = options[:port] || 80
41
58
  @server_name = options[:server_name]
42
- @sigkill = 'QUIT'
43
- @supports_rack = false
59
+ # Setting @sudo to nil will let the server apps' shells handle sudo
60
+ @sudo = options[:sudo] || @port < 1024 || nil
61
+ @target = options[:point_to] || @app
62
+
63
+ @supports_rack = false
64
+ @supports_passenger = false
44
65
  end
45
66
 
46
67
 
47
68
  ##
48
69
  # Check if passenger is required to run the application.
49
- # Returns true if the server's target is a Sunshine::App
70
+ # Returns true if the server's target is a Sunshine::App and if
71
+ # the server explicitely supports passenger.
50
72
 
51
73
  def use_passenger?
52
- Sunshine::App === @target && !supports_rack?
74
+ Sunshine::App === @target && supports_passenger? && !supports_rack?
53
75
  end
54
76
 
55
77
 
@@ -68,7 +90,7 @@ module Sunshine
68
90
 
69
91
 
70
92
  ##
71
- # Add passenger information to the binder at setup time.
93
+ # Adds passenger information to the binder at setup time.
72
94
 
73
95
  def setup
74
96
  super do |server_app, binder|
@@ -85,11 +107,10 @@ module Sunshine
85
107
 
86
108
 
87
109
  ##
88
- # Default server stop command.
110
+ # Defines if this server has passenger support.
89
111
 
90
- def stop_cmd
91
- "test -f #{@pid} && kill -#{@sigkill} $(cat #{@pid}) && sleep 1 && "+
92
- "rm -f #{@pid} || echo 'No #{@name} process to stop for #{@app.name}';"
112
+ def supports_passenger?
113
+ @supports_passenger
93
114
  end
94
115
 
95
116
 
@@ -108,6 +129,12 @@ module Sunshine
108
129
 
109
130
  binder.set :server_name, (@server_name || shell.host)
110
131
 
132
+ binder.set :proxy_name, "#{@app.name}_proxy"
133
+
134
+ binder.set :target_server do
135
+ target.server_name || server_name
136
+ end
137
+
111
138
  binder
112
139
  end
113
140
 
@@ -116,7 +143,7 @@ module Sunshine
116
143
  super
117
144
 
118
145
  @app.after_user_script do |app|
119
- next unless @port
146
+ next unless @port && has_setup?
120
147
 
121
148
  each_server_app do |sa|
122
149
  sa.info[:ports][@pid] = @port
@@ -0,0 +1,47 @@
1
+ module Sunshine
2
+
3
+ ##
4
+ # The ServerCluster is simply a fancy Array that conveniently forwards
5
+ # some method calls to each server in the array, namely:
6
+ # Server#setup, Server#start, Server#stop, Server#restart,
7
+ # Server#has_setup?, Server#status.
8
+
9
+ class ServerCluster < Array
10
+
11
+ ##
12
+ # ServerClusters get initialized just like any server class with the
13
+ # additional svr_class (Unicorn, Thin, Mongrel) and the number of
14
+ # server instances you would like:
15
+ #
16
+ # ServerCluster.new Mongrel, 3, app, :port => 5000
17
+ # #=> [<# mongrel_5000 >, <# mongrel_5001 >, <# mongrel_5002 >]
18
+ #
19
+ # ServerClusters can also be created from any Server class:
20
+ #
21
+ # Mongrel.new_cluster 3, app, :port => 5000
22
+
23
+ def initialize svr_class, count, app, options={}
24
+ count.times do |num|
25
+ port = (options[:port] || 80) + num
26
+ name = (options[:name] || svr_class.short_name) + ".#{port}"
27
+
28
+ self << svr_class.new(app, options.merge(:name => name, :port => port))
29
+ end
30
+ end
31
+
32
+
33
+ [:setup, :start, :stop, :restart].each do |method|
34
+ define_method method do
35
+ each{|server| server.send method }
36
+ end
37
+ end
38
+
39
+
40
+ [:has_setup?, :status].each do |method|
41
+ define_method method do
42
+ each{|server| return false unless server.send method}
43
+ true
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
1
+ module Sunshine
2
+
3
+ ##
4
+ # Simple server wrapper for Thin setup and control.
5
+ # Thin is considered a backend server and therefore does not support
6
+ # the :point_to proxying option.
7
+ #
8
+ # Note: Thin only supports a single log file. The default stdout file is used.
9
+ #
10
+ # Note: Thin manipulates the passed pid filepath to:
11
+ # path/[basename].[port].pid
12
+ # Sunshine::Thin will adjust the @pid attribute value accordingly.
13
+
14
+ class Thin < Server
15
+
16
+ def initialize app, options={}
17
+ super
18
+
19
+ @start_pid = @pid
20
+
21
+ pid_name = File.basename(@pid, ".pid")
22
+ @pid = File.join File.dirname(@pid), "#{pid_name}.#{@port}.pid"
23
+
24
+ @timeout = options[:timeout] || 3
25
+
26
+ @supports_rack = true
27
+ @supports_passenger = false
28
+ end
29
+
30
+
31
+ def start_cmd
32
+ "cd #{@app.current_path} && "+
33
+ "#{@bin} start -C #{self.config_file_path} -P #{@start_pid};"
34
+ end
35
+ end
36
+ end
@@ -2,6 +2,8 @@ module Sunshine
2
2
 
3
3
  ##
4
4
  # Simple server wrapper for Unicorn setup and control.
5
+ # Unicorn is strictly a backend server and therefore does not support
6
+ # the :point_to proxying option.
5
7
 
6
8
  class Unicorn < Server
7
9
 
@@ -10,7 +12,8 @@ module Sunshine
10
12
 
11
13
  @timeout = options[:timeout] || 3.0
12
14
 
13
- @supports_rack = true
15
+ @supports_rack = true
16
+ @supports_passenger = false
14
17
  end
15
18
 
16
19
 
@@ -10,6 +10,9 @@ Sunshine.dependencies.instance_eval do
10
10
  apt 'git', :pkg => 'git-core'
11
11
  yum 'git', :pkg => 'git-core'
12
12
 
13
+ apt 'rsync'
14
+ yum 'rsync'
15
+
13
16
  yum 'httpd-devel'
14
17
 
15
18
  apt 'apache2', :pkg => 'apache2-mpm-prefork'
@@ -30,10 +33,10 @@ Sunshine.dependencies.instance_eval do
30
33
  apt 'irb'
31
34
  yum 'irb', :pkg => 'ruby-irb'
32
35
 
33
- apt 'rubygems', :version => '1.3.5' do
36
+ apt 'rubygems' do
34
37
  requires 'ruby', 'ruby-devel'
35
38
  end
36
- yum 'rubygems', :version => '1.3.5' do
39
+ yum 'rubygems' do
37
40
  requires 'ruby', 'ruby-devel'
38
41
  end
39
42
 
@@ -111,7 +114,7 @@ Sunshine.dependencies.instance_eval do
111
114
 
112
115
 
113
116
  ##
114
- # Define gems used by Sunshine
117
+ # Define gems used by Sunshine remotely
115
118
 
116
119
  gem 'bundler', :version => ">=0.9"
117
120
 
@@ -121,6 +124,10 @@ Sunshine.dependencies.instance_eval do
121
124
 
122
125
  gem 'geminstaller', :version => ">=0.5"
123
126
 
127
+ gem 'mongrel', :version => ">=1.1.5"
128
+
129
+ gem 'thin', :version => ">=1.2.7"
130
+
124
131
  gem 'unicorn', :version => ">=0.9"
125
132
 
126
133
  gem 'rainbows', :version => ">=0.90.2"
@@ -17,8 +17,8 @@ module Sunshine
17
17
  # :uri_path:: The path that healthcheck will be used on.
18
18
  # :health_file:: The file to check for health.
19
19
  #
20
- # use SunshineHealth, :uri_path => "/health.txt",
21
- # :health_file => "health.txt"
20
+ # use Sunshine::Health, :uri_path => "/health.txt",
21
+ # :health_file => "health.txt"
22
22
 
23
23
  class Healthcheck
24
24
 
@@ -51,7 +51,7 @@ module Sunshine
51
51
  # The constructor also supports the following options:
52
52
  # :env:: hash - hash of environment variables to set for the ssh session
53
53
  # :password:: string - password for ssh login; if missing the deploy server
54
- # will attempt to prompt the user for a password.
54
+ # will attempt to prompt the user for a password.
55
55
 
56
56
  def initialize host, options={}
57
57
  super $stdout, options
@@ -78,7 +78,7 @@ module Sunshine
78
78
 
79
79
  ##
80
80
  # Runs a command via SSH. Optional block is passed the
81
- # stream(stderr, stdout) and string data
81
+ # stream(stderr, stdout) and string data.
82
82
 
83
83
  def call command_str, options={}, &block
84
84
  Sunshine.logger.info @host, "Running: #{command_str}" do
@@ -204,6 +204,9 @@ module Sunshine
204
204
 
205
205
  private
206
206
 
207
+ ##
208
+ # Figure out which rsync flags to use.
209
+
207
210
  def build_rsync_flags options
208
211
  flags = @rsync_flags.dup
209
212
 
@@ -222,12 +225,18 @@ module Sunshine
222
225
  end
223
226
 
224
227
 
228
+ ##
229
+ # Creates an rsync command.
230
+
225
231
  def rsync_cmd from_path, to_path, options={}
226
232
  cmd = ["rsync", build_rsync_flags(options), from_path, to_path]
227
233
  cmd.flatten.compact.join(" ")
228
234
  end
229
235
 
230
236
 
237
+ ##
238
+ # Wraps the command in an ssh call.
239
+
231
240
  def ssh_cmd string, options=nil
232
241
  options ||= {}
233
242
 
data/lib/sunshine/repo.rb CHANGED
@@ -62,7 +62,7 @@ module Sunshine
62
62
  def initialize url, options={}
63
63
  @scm = self.class.name.split("::").last.sub('Repo', '').downcase
64
64
 
65
- @url = url
65
+ @url = url.to_s
66
66
  @flags = [*options[:flags]].compact
67
67
  end
68
68
 
@@ -13,6 +13,7 @@ module Sunshine
13
13
  def initialize url, options={}
14
14
  super
15
15
  @flags << "-r"
16
+ @url << "/" unless @url[-1..-1] == "/"
16
17
  end
17
18
 
18
19
 
@@ -4,28 +4,29 @@ LoadModule authz_host_module modules/mod_authz_host.so
4
4
  ErrorLog <%= expand_path log_file(:stderr) %>
5
5
  TransferLog <%= expand_path log_file(:stdout) %>
6
6
 
7
- <% if use_passenger? %>
7
+ <% if use_passenger? -%>
8
8
  LoadModule passenger_module <%= passenger_root %>/ext/apache2/mod_passenger.so
9
9
 
10
10
  PassengerRuby <%= shell.call "which ruby" %>
11
11
  PassengerRoot <%= passenger_root %>
12
12
  PassengerMaxPoolSize <%= processes %>
13
- <% end %>
13
+ <% end -%>
14
14
 
15
- <% unless Sunshine::App === target %>
16
- LoadModule proxy_module modules/mod_proxy.so
17
- LoadModule proxy_connect_module modules/mod_proxy_connect.so
18
- LoadModule proxy_http_module modules/mod_proxy_http.so
19
- <% end %>
15
+ <% unless App === target -%>
16
+ LoadModule proxy_module modules/mod_proxy.so
17
+ LoadModule proxy_connect_module modules/mod_proxy_connect.so
18
+ LoadModule proxy_http_module modules/mod_proxy_http.so
19
+ LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
20
+ <% end -%>
20
21
 
21
22
 
22
23
  PidFile <%= expand_path pid %>
23
- MaxClients <%= max_clients %>
24
+ MaxClients <%= connections %>
24
25
 
25
- <% if sudo == true || sudo == 'root' %>
26
+ <% if sudo == true || sudo == 'root' -%>
26
27
  User nobody
27
28
  Group nobody
28
- <% end %>
29
+ <% end -%>
29
30
 
30
31
  Listen <%= port %>
31
32
 
@@ -36,19 +37,25 @@ NameVirtualHost *:<%= port %>
36
37
  ServerName <%= server_name %>
37
38
  ServerAlias www.<%= server_name %>
38
39
 
39
- <% if Sunshine::App === target %>
40
-
41
40
  DocumentRoot <%= expand_path app.current_path %>/public
42
41
 
43
42
  <Directory <%= app.current_path %>/public>
44
- Allow from all
45
- Options -MultiViews
43
+ Allow from all
44
+ Options -MultiViews
46
45
  </Directory>
47
46
 
48
- <% else %>
47
+ <% unless App === target -%>
49
48
 
50
- ProxyPass / http://0.0.0.0:<%= target.port %>/
51
- ProxyPassReverse / http://0.0.0.0:<%= target.port %>/
49
+ <Proxy balancer://<%= proxy_name %>>
50
+ <% [*target].each do |server| %>
51
+ BalancerMember http://0.0.0.0:<%= server.port %>
52
+ <% end -%>
53
+ </Proxy>
52
54
 
53
- <% end %>
55
+ # Using / after the proxy target is essential to the public dir.
56
+ ProxyPass / balancer://<%= proxy_name %>/ timeout=<%= timeout %>
57
+ ProxyPassReverse / balancer://<%= proxy_name %>/
58
+ ProxyPreserveHost On
59
+
60
+ <% end -%>
54
61
  </VirtualHost>