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
@@ -0,0 +1,9 @@
1
+ ---
2
+ :environment: <%= app.deploy_env %>
3
+ :host: 0.0.0.0
4
+ :num_processors: <%= connections %>
5
+ :port: <%= port %>
6
+ :daemon: true
7
+ :log_file: <%= expand_path log_file(:stdout) %>
8
+ :pid_file: <%= expand_path pid %>
9
+ :timeout: <%= timeout %>
@@ -8,7 +8,7 @@ pid <%= expand_path pid %>;
8
8
  error_log <%= expand_path log_file(:stderr) %> info;
9
9
 
10
10
  events {
11
- worker_connections 1024;
11
+ worker_connections <%= connections %>;
12
12
  <% unless darwin %>
13
13
  use epoll;
14
14
  <% end %>
@@ -25,6 +25,7 @@ http {
25
25
  client_body_temp_path <%= darwin ? '/var/tmp/nginx' : '/dev/shm' %>;
26
26
  proxy_temp_path <%= darwin ? '/var/tmp/nginx' : '/dev/shm' %>;
27
27
 
28
+ include <%= File.dirname shell.call("which nginx") %>/../conf/mime.types;
28
29
  default_type application/octet-stream;
29
30
 
30
31
  log_format sunshine '$remote_addr - $remote_user [$time_local] '
@@ -49,11 +50,13 @@ http {
49
50
  gzip_proxied any;
50
51
  gzip_types text/plain text/html text/css application/x-javascript application/xml application/xml+rss text/javascript;
51
52
 
52
- <% if Sunshine::Server === target %>
53
- upstream app_server {
54
- server 0:<%= target.port %> fail_timeout=<%= timeout %>;
53
+ <% unless App === target -%>
54
+ upstream <%= proxy_name %> {
55
+ <% [*target].each do |server| -%>
56
+ server 0:<%= server.port %> fail_timeout=<%= timeout %>;
57
+ <% end -%>
55
58
  }
56
- <% end %>
59
+ <% end -%>
57
60
 
58
61
  ##
59
62
  # 301 redirect www to non-www host.
@@ -82,9 +85,9 @@ http {
82
85
  ##
83
86
  # Main proxy
84
87
  location / {
85
- <% if darwin %>
88
+ <% if darwin -%>
86
89
  ssi on;
87
- <% end %>
90
+ <% end -%>
88
91
  if (-f $request_filename) {
89
92
  break;
90
93
  }
@@ -92,9 +95,9 @@ http {
92
95
  include <%= expand_path config_path %>/nginx_proxy.conf;
93
96
  expires -1;
94
97
 
95
- <% if Sunshine::Server === target %>
98
+ <% unless App === target %>
96
99
  if (!-f $request_filename) {
97
- proxy_pass http://app_server;
100
+ proxy_pass http://<%= proxy_name %>;
98
101
  break;
99
102
  }
100
103
  <% end %>
@@ -0,0 +1,12 @@
1
+ ---
2
+ address: 0.0.0.0
3
+ timeout: <%= timeout %>
4
+ port: <%= port %>
5
+ log: <%= expand_path log_file(:stdout) %>
6
+ max_conns: <%= connections %>
7
+ require: []
8
+
9
+ environment: <%= app.deploy_env %>
10
+ max_persistent_conns: <%= connections / 2 %>
11
+ servers: <%= processes %>
12
+ daemonize: true
@@ -0,0 +1,161 @@
1
+ module HelperMethods
2
+
3
+ def mock_app
4
+ Sunshine::App.new(TEST_APP_CONFIG_FILE).extend MockObject
5
+ end
6
+
7
+
8
+ def mock_remote_shell host=nil
9
+ host ||= "user@some_server.com"
10
+ remote_shell = Sunshine::RemoteShell.new host
11
+
12
+ remote_shell.extend MockOpen4
13
+ remote_shell.extend MockObject
14
+
15
+ use_remote_shell remote_shell
16
+
17
+ remote_shell.connect
18
+ remote_shell
19
+ end
20
+
21
+
22
+ def mock_svn_response url=nil
23
+ url ||= "svn://subversion/path/to/my_app/trunk"
24
+
25
+ svn_response = <<-STR
26
+ <?xml version="1.0"?>
27
+ <log>
28
+ <logentry
29
+ revision="777">
30
+ <author>user</author>
31
+ <date>2010-01-26T01:49:17.372152Z</date>
32
+ <msg>finished testing server.rb</msg>
33
+ </logentry>
34
+ </log>
35
+ STR
36
+
37
+ Sunshine::SvnRepo.extend(MockObject) unless
38
+ Sunshine::SvnRepo.is_a?(MockObject)
39
+
40
+ Sunshine::SvnRepo.mock :svn_log, :return => svn_response
41
+ Sunshine::SvnRepo.mock :get_svn_url, :return => url
42
+ end
43
+
44
+
45
+ def mock_remote_shell_popen4
46
+ Sunshine::RemoteShell.class_eval{ include MockOpen4 }
47
+ end
48
+
49
+
50
+ def set_mock_response_for obj, code, stream_vals={}, options={}
51
+ case obj
52
+ when Sunshine::App then
53
+ obj.each do |sa|
54
+ sa.shell.set_mock_response code, stream_vals, options
55
+ end
56
+ when Sunshine::ServerApp then
57
+ obj.shell.set_mock_response code, stream_vals, options
58
+ when Sunshine::RemoteShell then
59
+ obj.set_mock_response code, stream_vals, options
60
+ end
61
+ end
62
+
63
+
64
+ def assert_dep_install dep_name, type=Sunshine::Yum
65
+ prefered = type rescue nil
66
+ args = [{:call => @remote_shell, :prefer => prefered}]
67
+
68
+ dep = if Sunshine::Dependency === dep_name
69
+ dep_name
70
+ else
71
+ Sunshine.dependencies.get(dep_name, :prefer => prefered)
72
+ end
73
+
74
+
75
+ assert dep.method_called?(:install!, :args => args),
76
+ "Dependency '#{dep_name}' install was not called."
77
+ end
78
+
79
+
80
+ def assert_not_called *args
81
+ assert !@remote_shell.method_called?(:call, :args => [*args]),
82
+ "Command called by #{@remote_shell.host} but should't have:\n #{args[0]}"
83
+ end
84
+
85
+
86
+ def assert_server_call *args
87
+ assert @remote_shell.method_called?(:call, :args => [*args]),
88
+ "Command was not called by #{@remote_shell.host}:\n #{args[0]}"
89
+ end
90
+
91
+
92
+ def assert_bash_script name, cmds, check_value
93
+ cmds = cmds.map{|cmd| "(#{cmd})" }
94
+ cmds << "echo true"
95
+
96
+ bash = <<-STR
97
+ #!/bin/bash
98
+ if [ "$1" == "--no-env" ]; then
99
+ #{cmds.flatten.join(" && ")}
100
+ else
101
+ #{@app.root_path}/env #{@app.root_path}/#{name} --no-env
102
+ fi
103
+ STR
104
+
105
+ assert_equal bash, check_value
106
+ end
107
+
108
+
109
+ def assert_ssh_call expected, ds=@remote_shell, options={}
110
+ expected = ds.send(:ssh_cmd, expected, options).join(" ")
111
+
112
+ error_msg = "No such command in remote_shell log [#{ds.host}]\n#{expected}"
113
+ error_msg << "\n\n#{ds.cmd_log.select{|c| c =~ /^ssh/}.join("\n\n")}"
114
+
115
+ assert ds.cmd_log.include?(expected), error_msg
116
+ end
117
+
118
+
119
+ def assert_rsync from, to, ds=@remote_shell, sudo=false
120
+ received = ds.cmd_log.last
121
+
122
+ rsync_path = if sudo
123
+ path = ds.sudo_cmd('rsync', sudo).join(' ')
124
+ "--rsync-path='#{ path }' "
125
+ end
126
+
127
+ rsync_cmd = "rsync -azP #{rsync_path}-e \"ssh #{ds.ssh_flags.join(' ')}\""
128
+
129
+ error_msg = "No such command in remote_shell log [#{ds.host}]\n#{rsync_cmd}"
130
+ error_msg << "#{from.inspect} #{to.inspect}"
131
+ error_msg << "\n\n#{ds.cmd_log.select{|c| c =~ /^rsync/}.join("\n\n")}"
132
+
133
+ if Regexp === from
134
+ found = ds.cmd_log.select do |cmd|
135
+
136
+ cmd_from = cmd.split(" ")[-2]
137
+ cmd_to = cmd.split(" ").last
138
+
139
+ cmd_from =~ from && cmd_to == to && cmd.index(rsync_cmd) == 0
140
+ end
141
+
142
+ assert !found.empty?, error_msg
143
+ else
144
+ expected = "#{rsync_cmd} #{from} #{to}"
145
+ assert ds.cmd_log.include?(expected), error_msg
146
+ end
147
+ end
148
+
149
+
150
+ def use_remote_shell remote_shell
151
+ @remote_shell = remote_shell
152
+ end
153
+
154
+
155
+ def each_remote_shell app=@app
156
+ app.server_apps.each do |sa|
157
+ use_remote_shell sa.shell
158
+ yield(sa.shell) if block_given?
159
+ end
160
+ end
161
+ end
@@ -12,7 +12,7 @@ class TestDaemon < Test::Unit::TestCase
12
12
  end
13
13
 
14
14
 
15
- def test_missing_start_stop_cmd
15
+ def test_missing_start_cmd
16
16
  daemon = Sunshine::Daemon.new @app
17
17
 
18
18
  begin
@@ -21,13 +21,6 @@ class TestDaemon < Test::Unit::TestCase
21
21
  rescue Sunshine::CriticalDeployError => e
22
22
  assert_equal "@start_cmd undefined. Can't start daemon", e.message
23
23
  end
24
-
25
- begin
26
- daemon.stop_cmd
27
- raise "Should have thrown CriticalDeployError but didn't :("
28
- rescue Sunshine::CriticalDeployError => e
29
- assert_equal "@stop_cmd undefined. Can't stop daemon", e.message
30
- end
31
24
  end
32
25
 
33
26
  end
@@ -106,6 +106,6 @@ passenger (2.2.4)
106
106
  def stop_cmd svr
107
107
  "test -f #{svr.pid} && kill -#{svr.sigkill} $(cat #{svr.pid}) && "+
108
108
  "sleep 1 && rm -f #{svr.pid} || "+
109
- "echo 'No #{svr.name} process to stop for #{svr.app.name}';"
109
+ "echo 'Could not kill #{svr.name} pid for #{svr.app.name}';"
110
110
  end
111
111
  end
@@ -98,6 +98,22 @@ class TestServer < Test::Unit::TestCase
98
98
  end
99
99
 
100
100
 
101
+ def test_new_cluster
102
+ cluster = Sunshine::Server.new_cluster 3, @app, :port => 5000
103
+
104
+ assert_equal Sunshine::ServerCluster, cluster.class
105
+ assert Array === cluster
106
+ assert_equal 3, cluster.length
107
+
108
+ cluster.each_with_index do |server, index|
109
+ port = 5000 + index
110
+ assert_equal Sunshine::Server, server.class
111
+ assert_equal port, server.port
112
+ assert_equal "server.#{port}", server.name
113
+ end
114
+ end
115
+
116
+
101
117
  def test_start
102
118
  server = @rainbows
103
119
  @server_app.shell.mock :file?, :args => [server.config_file_path],
@@ -0,0 +1,46 @@
1
+ require 'test/test_helper'
2
+
3
+ class TestServerCluster < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @cluster =
7
+ Sunshine::ServerCluster.new Sunshine::Unicorn, 3, mock_app, :port => 2000
8
+
9
+ @cluster.each{|server| server.extend MockObject }
10
+ end
11
+
12
+
13
+ def test_initialize
14
+ name = "someserver.3333"
15
+
16
+ cluster =
17
+ Sunshine::ServerCluster.new Sunshine::Thin, 3, mock_app,
18
+ :port => 3000, :name => name
19
+
20
+ assert_equal Sunshine::ServerCluster, cluster.class
21
+ assert Array === cluster
22
+ assert_equal 3, cluster.length
23
+
24
+ cluster.each_with_index do |server, index|
25
+ port = 3000 + index
26
+ assert_equal Sunshine::Thin, server.class
27
+ assert_equal port, server.port
28
+ assert_equal "#{name}.#{port}", server.name
29
+ end
30
+ end
31
+
32
+
33
+ def test_forwarded_methods
34
+ [:has_setup?, :status, :setup, :start, :stop, :restart].each do |method|
35
+ @cluster.each do |server|
36
+ server.mock method, :return => true
37
+ end
38
+
39
+ @cluster.send method
40
+
41
+ @cluster.each do |server|
42
+ assert server.method_called?(method)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -28,12 +28,13 @@ class TestSunshine < Test::Unit::TestCase
28
28
 
29
29
  def test_find_command
30
30
  assert !Sunshine.find_command('st')
31
- assert_equal 'start', Sunshine.find_command('sta')
32
- assert_equal 'stop', Sunshine.find_command('sto')
33
- assert_equal 'add', Sunshine.find_command('a')
31
+ assert_equal Sunshine::StartCommand, Sunshine.find_command('sta')
32
+ assert_equal Sunshine::StopCommand, Sunshine.find_command('sto')
33
+ assert_equal Sunshine::AddCommand, Sunshine.find_command('a')
34
34
 
35
35
  Sunshine::COMMANDS.each do |cmd|
36
- assert_equal cmd, Sunshine.find_command(cmd)
36
+ const = Sunshine.const_get "#{cmd.capitalize}Command"
37
+ assert_equal const, Sunshine.find_command(cmd)
37
38
  end
38
39
  end
39
40
 
@@ -44,7 +45,7 @@ class TestSunshine < Test::Unit::TestCase
44
45
 
45
46
  Sunshine.run %w{run somefile.rb -l debug -e prod --no-trace}
46
47
 
47
- assert_command Sunshine::RunCommand, [['somefile.rb'], Sunshine.setup]
48
+ assert_command Sunshine::RunCommand, [['somefile.rb'], Sunshine.config]
48
49
  end
49
50
 
50
51
 
@@ -56,23 +57,28 @@ class TestSunshine < Test::Unit::TestCase
56
57
 
57
58
  mock_sunshine_command cmd
58
59
 
59
- Sunshine.run %w{thing1 thing2 -r remoteserver.com}.unshift(name)
60
+ argv = [name, 'thing1', 'thing2', '-r', 'remoteserver.com']
61
+
62
+ Sunshine.run argv
60
63
 
61
64
  servers = [Sunshine::RemoteShell.new("remoteserver.com")]
62
65
 
63
- args = [%w{thing1 thing2}, Sunshine.setup]
66
+ args = [%w{thing1 thing2}, Sunshine.config]
64
67
  assert_command cmd, args
65
68
 
66
- assert_equal servers, Sunshine.setup['servers']
69
+ assert_equal servers, Sunshine.config['servers']
70
+
71
+
72
+ argv = [name, 'thing1', 'thing2', '-v']
67
73
 
68
- Sunshine.run %w{thing1 thing2 -v}.unshift(name)
74
+ Sunshine.run argv
69
75
  servers = [Sunshine.shell]
70
76
 
71
- args = [%w{thing1 thing2}, Sunshine.setup]
77
+ args = [%w{thing1 thing2}, Sunshine.config]
72
78
  assert_command cmd, args
73
79
 
74
- assert_equal servers, Sunshine.setup['servers']
75
- assert Sunshine.setup['verbose']
80
+ assert_equal servers, Sunshine.config['servers']
81
+ assert Sunshine.config['verbose']
76
82
  end
77
83
  end
78
84
 
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
+ - 1
7
8
  - 0
8
- - 3
9
- version: 1.0.3
9
+ version: 1.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Jeremie Castagna
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-03-26 00:00:00 -07:00
17
+ date: 2010-04-02 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -88,12 +88,7 @@ dependencies:
88
88
  type: :development
89
89
  version_requirements: *id005
90
90
  description: |-
91
- Sunshine is an object-oriented api for rack application deployment.
92
-
93
- Sunshine is open and it can do a lot! It's meant to be dug into and understood.
94
- Knowing how it works will let you do really neat things: classes are
95
- decoupled as much as possible to allow for optimal flexibility,
96
- and most can be used independently.
91
+ Sunshine is a framework for rack and rails application deployment.
97
92
 
98
93
  This gem was made possible by the sponsoring of AT&T Interactive
99
94
  (http://attinteractive.com).
@@ -132,9 +127,12 @@ files:
132
127
  - lib/sunshine/daemons/apache.rb
133
128
  - lib/sunshine/daemons/ar_sendmail.rb
134
129
  - lib/sunshine/daemons/delayed_job.rb
130
+ - lib/sunshine/daemons/mongrel_rails.rb
135
131
  - lib/sunshine/daemons/nginx.rb
136
132
  - lib/sunshine/daemons/rainbows.rb
137
133
  - lib/sunshine/daemons/server.rb
134
+ - lib/sunshine/daemons/server_cluster.rb
135
+ - lib/sunshine/daemons/thin.rb
138
136
  - lib/sunshine/daemons/unicorn.rb
139
137
  - lib/sunshine/dependencies.rb
140
138
  - lib/sunshine/dependency_lib.rb
@@ -154,15 +152,18 @@ files:
154
152
  - lib/sunshine/shell.rb
155
153
  - templates/apache/apache.conf.erb
156
154
  - templates/logrotate/logrotate.conf.erb
155
+ - templates/mongrel_rails/mongrel_rails.conf.erb
157
156
  - templates/nginx/nginx.conf.erb
158
157
  - templates/nginx/nginx_optimize.conf
159
158
  - templates/nginx/nginx_proxy.conf
160
159
  - templates/rainbows/rainbows.conf.erb
161
160
  - templates/sunshine/middleware/health.rb
162
161
  - templates/sunshine/sunshine.rake
162
+ - templates/thin/thin.conf.erb
163
163
  - templates/unicorn/unicorn.conf.erb
164
164
  - test/fixtures/app_configs/test_app.yml
165
165
  - test/fixtures/sunshine_test/test_upload
166
+ - test/helper_methods.rb
166
167
  - test/mocks/mock_object.rb
167
168
  - test/mocks/mock_open4.rb
168
169
  - test/test_helper.rb
@@ -178,12 +179,13 @@ files:
178
179
  - test/unit/test_repo.rb
179
180
  - test/unit/test_server.rb
180
181
  - test/unit/test_server_app.rb
182
+ - test/unit/test_server_cluster.rb
181
183
  - test/unit/test_shell.rb
182
184
  - test/unit/test_sunshine.rb
183
185
  - test/unit/test_svn_repo.rb
184
186
  - test/unit/test_unicorn.rb
185
187
  has_rdoc: true
186
- homepage: http://betalabs.yellowpages.com/
188
+ homepage: http://github.com/yaksnrainbows/sunshine
187
189
  licenses: []
188
190
 
189
191
  post_install_message:
@@ -212,7 +214,7 @@ rubyforge_project: sunshine
212
214
  rubygems_version: 1.3.6
213
215
  signing_key:
214
216
  specification_version: 3
215
- summary: Sunshine is an object-oriented api for rack application deployment
217
+ summary: Sunshine is a framework for rack and rails application deployment
216
218
  test_files:
217
219
  - test/test_helper.rb
218
220
  - test/unit/test_app.rb
@@ -227,6 +229,7 @@ test_files:
227
229
  - test/unit/test_repo.rb
228
230
  - test/unit/test_server.rb
229
231
  - test/unit/test_server_app.rb
232
+ - test/unit/test_server_cluster.rb
230
233
  - test/unit/test_shell.rb
231
234
  - test/unit/test_sunshine.rb
232
235
  - test/unit/test_svn_repo.rb