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
@@ -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