sunshine 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,23 @@
1
+ === 1.1.2 / 2010-04-15
2
+
3
+ * Improvements:
4
+
5
+ * Added array support to App#find.
6
+
7
+ * Added auto registration of Dependency subclasses for scalability.
8
+
9
+ * Added call to App#start in App#deploy.
10
+
11
+ * Bugfixes:
12
+
13
+ * Fixed bug where env wouldn't be applied correctly in control scripts.
14
+
15
+ * Changed App#with_filter to actually change the App#server_apps attribute.
16
+
17
+ * Shell#env_cmd now returns a flattened array.
18
+
19
+ * Added actual check of running process to Daemon#status_cmd.
20
+
1
21
  === 1.1.1 / 2010-04-05
2
22
 
3
23
  * Improvements:
data/Manifest.txt CHANGED
@@ -3,9 +3,9 @@ Manifest.txt
3
3
  README.txt
4
4
  Rakefile
5
5
  bin/sunshine
6
- examples/deploy.rb
7
6
  examples/deploy_tasks.rake
8
7
  examples/standalone_deploy.rb
8
+ examples/sunshine_deploy.rb
9
9
  lib/commands/add.rb
10
10
  lib/commands/default.rb
11
11
  lib/commands/list.rb
data/README.txt CHANGED
@@ -17,16 +17,32 @@ This gem was made possible by the sponsoring of AT&T Interactive
17
17
 
18
18
  Installing sunshine:
19
19
 
20
- gem install sunshine
20
+ $ gem install sunshine
21
+
22
+ Call sunshine to create the config file:
23
+
24
+ $ sunshine
25
+
26
+ Missing config file was created for you: /Users/jsmith/.sunshine
27
+
28
+ ---
29
+ web_directory: /var/www
30
+ max_deploy_versions: 5
31
+ auto_dependencies: true
32
+ level: info
33
+ auto: false
34
+ remote_checkouts: false
35
+ deploy_env: :development
36
+
21
37
 
22
38
  You can either use sunshine by requiring the gem in your script, such as
23
39
  in a rakefile (which is more common):
24
40
 
25
- rake sunshine:deploy
41
+ $ rake sunshine:deploy
26
42
 
27
43
  Or you can also call built-in sunshine commands:
28
44
 
29
- sunshine run my_deploy.rb -e qa
45
+ $ sunshine run my_deploy.rb -e qa
30
46
 
31
47
 
32
48
  == Rake Deploy Tasks in 5 Minutes
@@ -43,7 +59,7 @@ specific deploy. Rake tasks are great for that, and Sunshine comes with a
43
59
  template rake file that you can modify to fit your needs.
44
60
 
45
61
  You can copy the template rake file to lib/tasks/ by running:
46
- sunshine --rakefile lib/tasks/.
62
+ $ sunshine --rakefile lib/tasks/.
47
63
 
48
64
  If you open the file, you'll see a variety of tasks that handle deployment, to
49
65
  application start/stop/restart-ing, to health checks. Most likely, the two tasks
@@ -86,8 +102,6 @@ to the @app.deploy block. Here's a sample of completed :app and :deploy tasks:
86
102
  rainbows.setup
87
103
  nginx.setup
88
104
  end
89
-
90
- @app.start :force => true
91
105
  end
92
106
 
93
107
  ...
@@ -217,6 +231,27 @@ In this example, :prod inherits top level values from :qa (only :repo in this
217
231
  instance). The :inherits key also supports an array as its value.
218
232
  All environments also inherit from the :default environment. The :default is
219
233
  also used if the app's deploy_env is not found in the config.
234
+
235
+ Finally, yaml configs get parsed by erb, exposing any options passed to the
236
+ App's constuctor, along with the deploy environment, letting your write configs
237
+ such as:
238
+
239
+ # deploy.rb
240
+
241
+ app = App.new "deploy.yml", :name => "my_app", :deploy_name => "release_001"
242
+
243
+
244
+ # deploy.yml
245
+ ---
246
+ :default :
247
+ :repo :
248
+ :type : svn
249
+ :url : svn://subversion/<%= name %>/tags/<%= deploy_name %>
250
+
251
+ :remote_shells :
252
+ - <%= deploy_env %>1.<%= name %>.domain.com
253
+ - <%= deploy_env %>2.<%= name %>.domain.com
254
+
220
255
  See Sunshine::App for more information.
221
256
 
222
257
 
@@ -237,8 +272,6 @@ Using Sunshine, this is most commonly defined as a part of the deploy process:
237
272
  nginx.setup
238
273
  end
239
274
 
240
- app.start :force => true
241
-
242
275
  When a new Server is instantiated and its setup method is run, it is added to
243
276
  the app's control scripts. This means that when the deploy is complete, those
244
277
  servers can be controlled by the app's start/stop/restart/status methods.
@@ -38,8 +38,6 @@ namespace :sunshine do
38
38
  rainbows.setup
39
39
  nginx.setup
40
40
  end
41
-
42
- @app.start :force => true
43
41
  end
44
42
 
45
43
 
@@ -15,8 +15,8 @@ Sunshine::App.deploy :name => 'my_app' do |app|
15
15
 
16
16
  app.run_geminstaller
17
17
 
18
- rainbows.restart
19
- nginx.restart
18
+ rainbows.setup
19
+ nginx.setup
20
20
  end
21
21
 
22
22
 
@@ -7,20 +7,17 @@ Sunshine::App.deploy do |app|
7
7
 
8
8
  app.gem_install 'isolate', :version => '1.3.0'
9
9
 
10
- app.install_deps 'libxml2-devel', 'libxslt-devel', 'libaio', 'sqlite-devel',
10
+ app.yum_install 'libxml2-devel', 'libxslt-devel', 'libaio', 'sqlite-devel',
11
11
  'sqlite', 'ruby-devel', 'activerecord-oracle_enhanced-adapter'
12
12
 
13
-
14
13
  app.run_bundler
15
14
 
16
-
17
15
  app.with_filter :role => :db do
18
16
 
19
17
  app.rake 'config/database.yml'
20
18
  app.rake 'db:migrate'
21
19
  end
22
20
 
23
-
24
21
  app.with_filter :role => :cdn do
25
22
 
26
23
  sass_yml_file = "#{app.checkout_path}/config/asset_packages.yml"
@@ -32,17 +29,14 @@ Sunshine::App.deploy do |app|
32
29
  end
33
30
 
34
31
 
35
- delayed_job = Sunshine::DelayedJob.new app
36
- delayed_job.restart
37
-
38
- mail = Sunshine::ARSendmail.new app
39
- mail.restart
32
+ Sunshine::DelayedJob.new(app).setup
33
+ Sunshine::ARSendmail.new(app).setup
40
34
 
41
35
  unicorn = Sunshine::Unicorn.new app, :port => 10001, :processes => 8
42
- unicorn.restart
36
+ nginx = Sunshine::Nginx.new app, :point_to => unicorn
43
37
 
44
- nginx = Sunshine::Nginx.new app, :point_to => unicorn, :port => 10000
45
- nginx.restart
38
+ unicorn.setup
39
+ nginx.setup
46
40
  end
47
41
 
48
42
 
data/lib/sunshine.rb CHANGED
@@ -19,7 +19,7 @@ module Sunshine
19
19
 
20
20
  ##
21
21
  # Sunshine version.
22
- VERSION = '1.1.1'
22
+ VERSION = '1.1.2'
23
23
 
24
24
  ##
25
25
  # Path to the list of installed sunshine apps.
@@ -36,13 +36,14 @@ module Sunshine
36
36
  ##
37
37
  # Default configuration.
38
38
  DEFAULT_CONFIG = {
39
- 'level' => 'info',
40
- 'deploy_env' => :development,
41
39
  'auto' => false,
42
- 'max_deploy_versions' => 5,
43
- 'web_directory' => '/var/www',
44
40
  'auto_dependencies' => true,
45
- 'remote_checkouts' => false
41
+ 'deploy_env' => :development,
42
+ 'level' => 'info',
43
+ 'max_deploy_versions' => 5,
44
+ 'remote_checkouts' => false,
45
+ 'timeout' => 300,
46
+ 'web_directory' => '/var/www'
46
47
  }
47
48
 
48
49
  ##
@@ -106,24 +107,6 @@ module Sunshine
106
107
  end
107
108
 
108
109
 
109
- ##
110
- # Handles input/output to the shell. See Sunshine::Shell.
111
-
112
- def self.shell
113
- @shell ||= Sunshine::Shell.new
114
- end
115
-
116
-
117
- ##
118
- # The default directory where apps should be deployed to:
119
- # '/var/www' by default. Overridden in the ~/.sunshine config file
120
- # or at setup time. See also App#deploy_path.
121
-
122
- def self.web_directory
123
- @config['web_directory']
124
- end
125
-
126
-
127
110
  ##
128
111
  # Should sunshine ever ask for user input? True by default; overridden with
129
112
  # the -a option.
@@ -143,7 +126,7 @@ module Sunshine
143
126
 
144
127
  ##
145
128
  # Maximum number of deploys (history) to keep on the remote server,
146
- # 5 by default. Overridden in the ~/.sunshine config file.
129
+ # 5 by default. Overridden in the config.
147
130
 
148
131
  def self.max_deploy_versions
149
132
  @config['max_deploy_versions']
@@ -152,13 +135,31 @@ module Sunshine
152
135
 
153
136
  ##
154
137
  # Check if the codebase should be checked out remotely, or checked out
155
- # locally and rsynced up. Overridden in the ~/.sunshine config file.
138
+ # locally and rsynced up. Overridden in the config.
156
139
 
157
140
  def self.remote_checkouts?
158
141
  @config['remote_checkouts']
159
142
  end
160
143
 
161
144
 
145
+ ##
146
+ # Handles input/output to the shell. See Sunshine::Shell.
147
+
148
+ def self.shell
149
+ @shell ||= Sunshine::Shell.new
150
+ end
151
+
152
+
153
+ ##
154
+ # How long to wait on a command to finish when no output is received.
155
+ # Defaults to 300 (seconds). Overridden in the config.
156
+ # Set to false to disable timeout.
157
+
158
+ def self.timeout
159
+ @config['timeout']
160
+ end
161
+
162
+
162
163
  ##
163
164
  # Check if trace log should be output at all.
164
165
  # This value can be assigned by default in ~/.sunshine
@@ -170,6 +171,16 @@ module Sunshine
170
171
  end
171
172
 
172
173
 
174
+ ##
175
+ # The default directory where apps should be deployed to:
176
+ # '/var/www' by default. Overridden in the config.
177
+ # See also App#deploy_path.
178
+
179
+ def self.web_directory
180
+ @config['web_directory']
181
+ end
182
+
183
+
173
184
  ##
174
185
  # Adds an INT signal trap with its description on the stack.
175
186
  # Returns a trap_item Array.
@@ -207,7 +218,6 @@ module Sunshine
207
218
  end
208
219
 
209
220
 
210
-
211
221
  ##
212
222
  # Global value of sudo to use. Returns true, nil, or a username.
213
223
  # This value can be assigned by default in ~/.sunshine
@@ -356,10 +366,15 @@ module Sunshine
356
366
  require 'sunshine/exceptions'
357
367
 
358
368
  require 'sunshine/shell'
369
+ require 'sunshine/remote_shell'
370
+
359
371
  require 'sunshine/output'
360
372
 
361
373
  require 'sunshine/binder'
362
374
 
375
+ require 'sunshine/server_app'
376
+ require 'sunshine/app'
377
+
363
378
  require 'sunshine/dependency_lib'
364
379
  require 'sunshine/package_managers/dependency'
365
380
  require 'sunshine/package_managers/apt'
@@ -387,11 +402,6 @@ module Sunshine
387
402
 
388
403
  require 'sunshine/healthcheck'
389
404
 
390
- require 'sunshine/remote_shell'
391
-
392
- require 'sunshine/server_app'
393
- require 'sunshine/app'
394
-
395
405
  require 'commands/default'
396
406
  require 'commands/list'
397
407
  require 'commands/add'
data/lib/sunshine/app.rb CHANGED
@@ -178,8 +178,6 @@ module Sunshine
178
178
 
179
179
  @deploy_name = options[:deploy_name] || Time.now.to_i.to_s
180
180
 
181
- @server_app_filter = nil
182
-
183
181
  set_deploy_paths options[:root_path]
184
182
 
185
183
  @server_apps = server_apps_from_config options[:remote_shells]
@@ -195,6 +193,8 @@ module Sunshine
195
193
  shell_env options[:shell_env]
196
194
 
197
195
  @post_user_lambdas = []
196
+
197
+ @has_session = false
198
198
  end
199
199
 
200
200
 
@@ -213,9 +213,10 @@ module Sunshine
213
213
  # Connect server apps. Supports any App#find options.
214
214
 
215
215
  def connect options=nil
216
- with_server_apps options,
217
- :msg => "Connecting..." do |server_app|
218
- server_app.shell.connect
216
+ Sunshine.logger.info :app, "Connecting..." do
217
+ threaded_each options do |server_app|
218
+ server_app.shell.connect
219
+ end
219
220
  end
220
221
  end
221
222
 
@@ -224,7 +225,7 @@ module Sunshine
224
225
  # Check if server apps are connected. Supports any App#find options.
225
226
 
226
227
  def connected? options=nil
227
- with_server_apps options, :no_threads => true do |server_app|
228
+ each options do |server_app|
228
229
  return false unless server_app.shell.connected?
229
230
  end
230
231
 
@@ -236,9 +237,10 @@ module Sunshine
236
237
  # Disconnect server apps. Supports any App#find options.
237
238
 
238
239
  def disconnect options=nil
239
- with_server_apps options,
240
- :msg => "Disconnecting..." do |server_app|
241
- server_app.shell.disconnect
240
+ Sunshine.logger.info :app, "Disconnecting..." do
241
+ threaded_each options do |server_app|
242
+ server_app.shell.disconnect
243
+ end
242
244
  end
243
245
  end
244
246
 
@@ -246,6 +248,12 @@ module Sunshine
246
248
  ##
247
249
  # Deploy the application to deploy servers and
248
250
  # call user's post-deploy code. Supports any App#find options.
251
+ #
252
+ # Note: The deploy method will stop the former deploy just before
253
+ # symlink and the passed block is run.
254
+ #
255
+ # Note: Once deployment is complete, the deploy method will attempt to
256
+ # run App#start.
249
257
 
250
258
  def deploy options=nil
251
259
  prev_connection = connected?
@@ -280,6 +288,8 @@ module Sunshine
280
288
 
281
289
  register_as_deployed
282
290
  remove_old_deploys
291
+
292
+ start :force => true
283
293
  end
284
294
  end
285
295
 
@@ -473,23 +483,21 @@ module Sunshine
473
483
  # find :user => 'db'
474
484
  # find :host => 'someserver.com'
475
485
  # find :role => :web
486
+ #
487
+ # The find method also supports passing arrays and will match
488
+ # any server app that matches any one condition:
489
+ # find :user => ['root', 'john']
490
+ #
491
+ # Returns all server apps who's user is either 'root' or 'john'.
476
492
 
477
493
  def find query=nil
478
- if @server_app_filter
479
- if Hash === query && Hash === @server_app_filter
480
- query.merge! @server_app_filter
481
- else
482
- query = @server_app_filter
483
- end
484
- end
485
-
486
494
  return @server_apps if query.nil? || query == :all
487
495
 
488
496
  @server_apps.select do |sa|
489
- next unless sa.shell.user == query[:user] if query[:user]
490
- next unless sa.shell.host == query[:host] if query[:host]
497
+ next unless [*query[:user]].include? sa.shell.user if query[:user]
498
+ next unless [*query[:host]].include? sa.shell.host if query[:host]
491
499
 
492
- next unless sa.has_roles?(query[:role]) if query[:role]
500
+ next unless sa.has_roles?(query[:role], true) if query[:role]
493
501
 
494
502
  true
495
503
  end
@@ -514,22 +522,6 @@ module Sunshine
514
522
  end
515
523
 
516
524
 
517
- %w{gem yum apt}.each do |dep_type|
518
- self.class_eval <<-STR, __FILE__, __LINE__ + 1
519
- ##
520
- # Install one or more #{dep_type} packages.
521
- # See Settler::#{dep_type.capitalize}#new for supported options.
522
-
523
- def #{dep_type}_install(*names)
524
- options = names.last if Hash === names.last
525
- with_server_apps options,
526
- :msg => "Installing #{dep_type} packages",
527
- :send => [:#{dep_type}_install, *names]
528
- end
529
- STR
530
- end
531
-
532
-
533
525
  ##
534
526
  # Gets or sets the healthcheck state. Returns a hash of host/state
535
527
  # pairs. State values are :enabled, :disabled, and :down. The method
@@ -711,7 +703,7 @@ module Sunshine
711
703
 
712
704
  def run_post_user_lambdas
713
705
  Sunshine.logger.info :app, "Running post deploy lambdas" do
714
- @post_user_lambdas.each{|l| l.call self}
706
+ with_session{ @post_user_lambdas.each{|l| l.call self} }
715
707
  end
716
708
  end
717
709
 
@@ -819,23 +811,29 @@ module Sunshine
819
811
  def threaded_each(options=nil, &block)
820
812
  mutex = Mutex.new
821
813
  threads = []
814
+ error = nil
822
815
 
823
816
  return_val = each(options) do |server_app|
824
817
 
825
818
  thread = Thread.new do
826
819
  server_app.shell.with_mutex mutex do
827
- yield server_app
820
+
821
+ begin
822
+ yield server_app
823
+
824
+ rescue => e
825
+ error = e
826
+ end
828
827
  end
829
828
  end
830
829
 
831
- # We don't want deploy servers to keep doing things if one fails
832
- thread.abort_on_exception = true
833
-
834
830
  threads << thread
835
831
  end
836
832
 
837
833
  threads.each{|t| t.join }
838
834
 
835
+ raise error if error
836
+
839
837
  return_val
840
838
  end
841
839
 
@@ -870,17 +868,19 @@ module Sunshine
870
868
  # end
871
869
 
872
870
  def with_filter filter_hash
873
- old_filter, @server_app_filter = @server_app_filter, filter_hash
871
+ old_server_apps, @server_apps = @server_apps, find(filter_hash)
874
872
 
875
873
  yield self
876
874
 
877
- @server_app_filter = old_filter
875
+ ensure
876
+ @server_apps = old_server_apps
878
877
  end
879
878
 
880
879
 
881
880
  ##
882
881
  # Calls a method for server_apps found with the passed options,
883
- # and with an optional log message. Supports all App#find
882
+ # and with an optional log message. Will attempt to run the methods in
883
+ # a session to avoid multiple ssh login prompts. Supports all App#find
884
884
  # options, plus:
885
885
  # :no_threads:: bool - disable threaded execution
886
886
  # :msg:: "some message" - log message
@@ -912,11 +912,13 @@ module Sunshine
912
912
  end
913
913
 
914
914
 
915
- if message
916
- Sunshine.logger.info(:app, message, &block)
915
+ with_session options do
916
+ if message
917
+ Sunshine.logger.info(:app, message, &block)
917
918
 
918
- else
919
- block.call
919
+ else
920
+ block.call
921
+ end
920
922
  end
921
923
  end
922
924
 
@@ -927,11 +929,13 @@ module Sunshine
927
929
  # already exists. Supports all App#find options.
928
930
 
929
931
  def with_session options=nil
930
- prev_connection = connected? options
932
+
933
+ prev_connection = connected?(options)
931
934
  connect options unless prev_connection
932
935
 
933
936
  yield
934
937
 
938
+ ensure
935
939
  disconnect options unless prev_connection
936
940
  end
937
941
 
@@ -285,7 +285,7 @@ module Sunshine
285
285
  # Get the command to check if the daemon is running.
286
286
 
287
287
  def status_cmd
288
- @status_cmd || "test -f #{@pid}"
288
+ @status_cmd || "test -f #{@pid} && kill -0 $(cat #{@pid})"
289
289
  end
290
290
 
291
291
 
@@ -425,7 +425,7 @@ module Sunshine
425
425
  :flags => '--chmod=ugo=rwx'
426
426
 
427
427
 
428
- cmd = sa.shell.sudo_cmd script_file, sudo
428
+ cmd = sa.shell.sudo_cmd "#{@app.root_path}/env #{script_file}", sudo
429
429
 
430
430
  sa.scripts[script.to_sym] << [*cmd].join(" ")
431
431
  end
@@ -9,6 +9,8 @@ module Sunshine
9
9
 
10
10
  class Apache < Server
11
11
 
12
+ attr_accessor :rails_base_uri
13
+
12
14
  def initialize app, options={}
13
15
  super
14
16
 
@@ -21,6 +23,8 @@ module Sunshine
21
23
 
22
24
  @connections = options[:connections] || 256
23
25
 
26
+ @rails_base_uri = options[:rails_base_uri]
27
+
24
28
  @timeout = 1 if @timeout < 1
25
29
 
26
30
  @dep_name = options[:dep_name] ||
@@ -35,7 +39,7 @@ module Sunshine
35
39
 
36
40
  def setup
37
41
  super do |server_app, binder|
38
- binder.set :max_clients, @max_clients
42
+ binder.set :rails_base_uri, @rails_base_uri
39
43
  yield(server_app, binder) if block_given?
40
44
  end
41
45
  end
@@ -79,13 +79,7 @@ module Sunshine
79
79
  # Gets the root of the installer passenger gem.
80
80
 
81
81
  def self.passenger_root shell
82
- str = shell.call "gem list passenger -d"
83
- version = $1 if str =~ /passenger\s\((.*)\)$/
84
- gempath = $1 if str =~ /Installed\sat:\s(.*)$/
85
-
86
- return unless version && gempath
87
-
88
- File.join(gempath, "gems/passenger-#{version}")
82
+ shell.call "passenger-config --root"
89
83
  end
90
84
 
91
85
 
@@ -20,8 +20,6 @@ Sunshine.dependencies do
20
20
  apt 'nginx'
21
21
  yum 'nginx'
22
22
 
23
- apt 'logrotate'
24
- yum 'logrotate'
25
23
 
26
24
  apt 'ruby', :pkg => 'ruby-full'
27
25
  yum 'ruby'
@@ -29,33 +27,9 @@ Sunshine.dependencies do
29
27
  apt 'ruby-devel', :pkg => 'ruby-dev'
30
28
  yum 'ruby-devel'
31
29
 
32
- apt 'irb'
33
- yum 'irb', :pkg => 'ruby-irb'
34
30
 
35
- apt 'rubygems' do
36
- requires 'ruby', 'ruby-devel'
37
- end
38
- yum 'rubygems' do
39
- requires 'ruby', 'ruby-devel'
40
- end
41
-
42
- apt 'logrotate'
43
- yum 'logrotate'
44
-
45
- apt 'curl-devel', :pkg => 'libcurl-dev'
46
- yum 'curl-devel'
47
-
48
- apt 'libxml2-devel', :pkg => 'libxml2-dev'
49
- yum 'libxml2-devel'
50
-
51
- apt 'libxslt-devel', :pkg => 'libxslt-dev'
52
- yum 'libxslt-devel'
53
-
54
- apt 'sqlite', :pkg => 'sqlite3'
55
- yum 'sqlite'
56
-
57
- apt 'sqlite-devel', :pkg => 'libsqlite3-dev'
58
- yum 'sqlite-devel'
31
+ apt 'rubygems', :requires => ['ruby', 'ruby-devel']
32
+ yum 'rubygems', :requires => ['ruby', 'ruby-devel']
59
33
 
60
34
 
61
35
  ##
@@ -117,8 +91,6 @@ Sunshine.dependencies do
117
91
 
118
92
  gem 'bundler', :version => ">=0.9"
119
93
 
120
- gem 'isolate', :version => ">=1.3.0"
121
-
122
94
  gem 'rake', :version => ">=0.8"
123
95
 
124
96
  gem 'geminstaller', :version => ">=0.5"
@@ -47,6 +47,34 @@ module Sunshine
47
47
  end
48
48
 
49
49
 
50
+ ##
51
+ # Registers a new dependency class, creates its constructor method
52
+ # (DependencyLib#[dep_class.short_name]).
53
+
54
+ def self.register_type dep_class
55
+ class_eval <<-STR, __FILE__, __LINE__ + 1
56
+
57
+ def #{dep_class.short_name}(name, options={}, &block)
58
+ dep = #{dep_class}.new(name, options.merge(:tree => self), &block)
59
+ self.add dep
60
+ dep
61
+ end
62
+ STR
63
+
64
+ dependency_types << dep_class
65
+ end
66
+
67
+
68
+ ##
69
+ # Define if sudo should be used
70
+
71
+ def self.sudo= value
72
+ dependency_types.each do |dep_class|
73
+ dep_class.sudo = value
74
+ end
75
+ end
76
+
77
+
50
78
  attr_reader :dependencies
51
79
 
52
80
  def initialize
@@ -186,15 +214,5 @@ module Sunshine
186
214
  dep.send method, options
187
215
  end
188
216
  end
189
-
190
-
191
- ##
192
- # Define if sudo should be used
193
-
194
- def self.sudo= value
195
- dependency_types.each do |dep_class|
196
- dep_class.sudo = value
197
- end
198
- end
199
217
  end
200
218
  end
@@ -317,8 +317,13 @@ module Sunshine
317
317
  end
318
318
 
319
319
 
320
- def self.short_class_name str
321
- str.to_s.split(":").last
320
+ ##
321
+ # Returns an underscored short version of the class name:
322
+ # Sunshine::Yum.short_name
323
+ # #=> "yum"
324
+
325
+ def self.short_name
326
+ @short_name ||= underscore self.name.to_s.split(":").last
322
327
  end
323
328
 
324
329
 
@@ -328,19 +333,13 @@ module Sunshine
328
333
  end
329
334
 
330
335
 
331
- def self.inherited(subclass)
332
- class_name = short_class_name subclass.to_s
333
- method_name = underscore class_name
334
-
335
- DependencyLib.class_eval <<-STR, __FILE__, __LINE__ + 1
336
- def #{method_name}(name, options={}, &block)
337
- dep = #{class_name}.new(name, options.merge(:tree => self), &block)
338
- self.add dep
339
- dep
340
- end
341
- STR
336
+ ##
337
+ # Auto register the new Dependency class with DependencyLib and ServerApp
338
+ # when inherited.
342
339
 
343
- DependencyLib.dependency_types << subclass
340
+ def self.inherited subclass
341
+ DependencyLib.register_type subclass
342
+ ServerApp.register_dependency_type subclass
344
343
  end
345
344
 
346
345
  inherited self
@@ -212,8 +212,6 @@ module Sunshine
212
212
  end
213
213
 
214
214
 
215
- private
216
-
217
215
  ##
218
216
  # Figure out which rsync flags to use.
219
217
 
data/lib/sunshine/repo.rb CHANGED
@@ -22,7 +22,7 @@ module Sunshine
22
22
  # Turn Sunshine::ScmNameRepo into :scm_name
23
23
  class_key = subclass.to_s.split("::").last
24
24
  class_key = $1 if class_key =~ /(\w+)Repo$/
25
- class_key.gsub! /([a-z0-9])([A-Z])/, '\1_\2'
25
+ class_key.gsub!(/([a-z0-9])([A-Z])/, '\1_\2')
26
26
  class_key = class_key.downcase
27
27
 
28
28
  @@repo_types[class_key] = subclass
@@ -47,6 +47,33 @@ module Sunshine
47
47
  end
48
48
 
49
49
 
50
+ ##
51
+ # Creates dependency instance methods such as gem_install, yum_install, etc
52
+ # on both App and ServerApp classes.
53
+
54
+ def self.register_dependency_type dep_class
55
+ class_eval <<-STR, __FILE__, __LINE__ + 1
56
+ def #{dep_class.short_name}_install(*names)
57
+ options = Hash === names.last ? names.delete_at(-1) : Hash.new
58
+
59
+ names.each do |name|
60
+ dep = #{dep_class}.new(name, options)
61
+ dep.install! :call => @shell
62
+ end
63
+ end
64
+ STR
65
+
66
+ App.class_eval <<-STR, __FILE__, __LINE__ + 1
67
+ def #{dep_class.short_name}_install(*names)
68
+ options = names.last if Hash === names.last
69
+ with_server_apps options,
70
+ :msg => "Installing #{dep_class.short_name} packages",
71
+ :send => [:#{dep_class.short_name}_install, *names]
72
+ end
73
+ STR
74
+ end
75
+
76
+
50
77
  app_attr :name, :deploy_name
51
78
  app_attr :root_path, :checkout_path, :current_path
52
79
  app_attr :deploys_path, :log_path, :shared_path
@@ -193,7 +220,7 @@ module Sunshine
193
220
 
194
221
 
195
222
  ##
196
- # Returns information about the deploy at hand.
223
+ # Builds a hash with information about the deploy at hand.
197
224
 
198
225
  def get_deploy_info
199
226
  { :deployed_at => Time.now.to_s,
@@ -231,33 +258,22 @@ module Sunshine
231
258
 
232
259
  ##
233
260
  # Check if this server app includes the specified roles:
261
+ # server_app.has_roles? :web
262
+ # server_app.has_roles? [:web, :app]
263
+ #
264
+ # The boolean operator may be changed to OR by passing true as the
265
+ # second argument:
266
+ # server_app.roles = [:web, :app]
267
+ # server_app.has_roles? [:web, :db] #=> false
268
+ # server_app.has_roles? [:web, :db], true #=> true
234
269
 
235
- def has_roles? *roles
236
- return true if @roles.include? :all
237
-
238
- roles.each do |role|
239
- return false unless @roles.include? role
240
- end
241
-
242
- true
243
- end
244
-
245
-
246
- %w{gem yum apt}.each do |dep_type|
247
- self.class_eval <<-STR, __FILE__, __LINE__ + 1
248
- ##
249
- # Install one or more #{dep_type} packages.
250
- # See #{dep_type.capitalize}#new for supported options.
270
+ def has_roles? roles, match_any=false
271
+ roles = [*roles]
251
272
 
252
- def #{dep_type}_install(*names)
253
- options = Hash === names.last ? names.delete_at(-1) : Hash.new
273
+ return true if @roles.include? :all
274
+ return !(roles & @roles).empty? if match_any
254
275
 
255
- names.each do |name|
256
- dep = #{dep_type.capitalize}.new(name, options)
257
- dep.install! :call => @shell
258
- end
259
- end
260
- STR
276
+ (roles & @roles).length == roles.length
261
277
  end
262
278
 
263
279
 
@@ -7,11 +7,7 @@ module Sunshine
7
7
 
8
8
  include Open4
9
9
 
10
- class TimeoutError < FatalDeployError; end
11
-
12
- ##
13
- # Time to wait with no activity until giving up on a command.
14
- TIMEOUT = 120
10
+ class TimeoutError < CriticalDeployError; end
15
11
 
16
12
  LOCAL_USER = `whoami`.chomp
17
13
  LOCAL_HOST = `hostname`.chomp
@@ -20,7 +16,7 @@ module Sunshine
20
16
  SUDO_PROMPT = /^Password:/
21
17
 
22
18
  attr_reader :user, :host, :password, :input, :output, :mutex
23
- attr_accessor :env, :sudo
19
+ attr_accessor :env, :sudo, :timeout
24
20
 
25
21
  def initialize output = $stdout, options={}
26
22
  @output = output
@@ -35,6 +31,8 @@ module Sunshine
35
31
  @env = options[:env] || {}
36
32
  @password = options[:password]
37
33
 
34
+ @timeout = options[:timeout] || Sunshine.timeout
35
+
38
36
  @cmd_activity = nil
39
37
 
40
38
  @mutex = nil
@@ -153,7 +151,8 @@ module Sunshine
153
151
  # Prompt the user for a password
154
152
 
155
153
  def prompt_for_password
156
- @password = ask("#{@user}@#{@host} Password:") do |q|
154
+ host_info = [@user, @host].compact.join("@")
155
+ @password = ask("#{host_info} Password:") do |q|
157
156
  q.echo = false
158
157
  end
159
158
  end
@@ -165,7 +164,7 @@ module Sunshine
165
164
  def env_cmd cmd, env_hash=@env
166
165
  if env_hash && !env_hash.empty?
167
166
  env_vars = env_hash.map{|e| e.join("=")}
168
- cmd = ["env", env_vars, cmd]
167
+ cmd = ["env", env_vars, cmd].flatten
169
168
  end
170
169
  cmd
171
170
  end
@@ -236,7 +235,8 @@ module Sunshine
236
235
  ##
237
236
  # Checks if timeout occurred.
238
237
 
239
- def timed_out? start_time=@cmd_activity, max_time=TIMEOUT
238
+ def timed_out? start_time=@cmd_activity, max_time=@timeout
239
+ return unless max_time
240
240
  Time.now.to_i - start_time.to_i > max_time
241
241
  end
242
242
 
@@ -39,11 +39,15 @@ NameVirtualHost *:<%= port %>
39
39
 
40
40
  DocumentRoot <%= expand_path app.current_path %>/public
41
41
 
42
- <Directory <%= app.current_path %>/public>
42
+ <Directory <%= expand_path app.current_path %>/public>
43
+ Options FollowSymLinks -MultiViews
44
+ AllowOverride None
45
+ Order allow,deny
43
46
  Allow from all
44
- Options -MultiViews
45
47
  </Directory>
46
48
 
49
+ <%= "RailsBaseURI #{rails_base_uri}" if rails_base_uri %>
50
+
47
51
  <% unless App === target -%>
48
52
 
49
53
  <Proxy balancer://<%= proxy_name %>>
@@ -11,7 +11,7 @@ module Sunshine
11
11
  ##
12
12
  # The healthcheck-enabled file.
13
13
 
14
- HEALTHCHECK_FILE = 'health.enabled'
14
+ HEALTHCHECK_FILE = '../../health.enabled'
15
15
 
16
16
 
17
17
  ##
@@ -1,5 +1,3 @@
1
- require 'sunshine'
2
-
3
1
  namespace :sunshine do
4
2
 
5
3
  ##
@@ -8,8 +6,10 @@ namespace :sunshine do
8
6
  # ...
9
7
  # end
10
8
 
11
- desc "Instantiate Sunshine"
9
+ desc "Instantiate app"
12
10
  task :app do
11
+ require 'sunshine'
12
+
13
13
  deploy_env = ENV['env'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
14
14
 
15
15
  Sunshine.setup 'deploy_env' => ( deploy_env || "development" )
@@ -29,18 +29,6 @@ namespace :sunshine do
29
29
  task :deploy => :app do
30
30
  Sunshine.setup 'trace' => true
31
31
 
32
- # If you're not able to add your public key to remote servers,
33
- # you can setup your tasks to use the App#with_session method
34
- # to avoid having to login multiple times:
35
- #
36
- # @app.with_session do
37
- # @app.deploy do |app|
38
- # ...
39
- # end
40
- #
41
- # @app.start :force => true
42
- # end
43
-
44
32
  @app.deploy do |app|
45
33
 
46
34
  # Do deploy-specific stuff here, e.g.
@@ -52,8 +40,36 @@ namespace :sunshine do
52
40
  # unicorn.setup
53
41
 
54
42
  end
43
+ end
44
+
55
45
 
56
- @app.start :force => true
46
+ desc "Sets up deploy servers"
47
+ task :setup => :app do
48
+ Sunshine.setup 'trace' => true
49
+
50
+ # Setup servers here
51
+ #
52
+ # @app.with_filter :role => :app do |app|
53
+ # app.yum_install 'libxml2', 'libxml2-devel'
54
+ # app.gem_install 'mechanize'
55
+ # end
56
+ #
57
+ # @app.with_filter :role => :db do |app|
58
+ # app.yum_install 'sqlite'
59
+ # app.gem_install 'sqlite3'
60
+ # end
61
+ #
62
+ # If you're not able to add your public key to remote servers,
63
+ # you can setup your tasks to use the App#with_session method
64
+ # to avoid having to login multiple times:
65
+ #
66
+ # @app.with_session do
67
+ # @app.with_filter :role => :app do |app|
68
+ # app.yum_install 'libxml2', 'libxml2-devel'
69
+ # app.gem_install 'mechanize'
70
+ # end
71
+ # ...
72
+ # end
57
73
  end
58
74
 
59
75
 
@@ -10,7 +10,8 @@ class TestApp < Test::Unit::TestCase
10
10
  :name => "app_name",
11
11
  :remote_checkout => true,
12
12
  :repo => {:type => "svn", :url => @svn_url},
13
- :remote_shells => ["user@some_server.com"],
13
+ :remote_shells => ["user@some_server.com",
14
+ ["server2.com", {:roles => "web db"}]],
14
15
  :root_path => "/usr/local/my_user/app_name"}
15
16
 
16
17
  @app = Sunshine::App.new @config
@@ -343,7 +344,6 @@ class TestApp < Test::Unit::TestCase
343
344
 
344
345
  @app.install_deps 'rake', bundler_dep
345
346
 
346
-
347
347
  each_remote_shell do |ds|
348
348
  [rake_dep, bundler_dep].each do |dep|
349
349
 
@@ -486,6 +486,28 @@ class TestApp < Test::Unit::TestCase
486
486
  end
487
487
 
488
488
 
489
+ def test_threaded_each_errors
490
+ err_host = "some_server.com"
491
+ finished = 0
492
+
493
+ @app.threaded_each do |server_app|
494
+ if server_app.shell.host == err_host
495
+ raise Sunshine::CriticalDeployError, server_app.shell.host
496
+ else
497
+ finished = finished.next
498
+ end
499
+ end
500
+
501
+ raise "Didn't raise threaded error when it should have"
502
+
503
+ rescue Sunshine::CriticalDeployError => e
504
+ host = @app.server_apps.first.shell.host
505
+
506
+ assert_equal host, e.message
507
+ assert_equal (@app.server_apps.length - 1), finished
508
+ end
509
+
510
+
489
511
  def test_upload_tasks
490
512
  path = "/path/to/tasks"
491
513
 
@@ -493,15 +515,17 @@ class TestApp < Test::Unit::TestCase
493
515
  :host => 'some_server.com',
494
516
  :remote_path => path
495
517
 
496
- each_remote_shell do |ds|
497
- assert_ssh_call "mkdir -p /path/to/tasks"
518
+ shell = @app.find(:host => 'some_server.com').first.shell
498
519
 
499
- %w{common tpkg}.each do |task|
500
- from = "#{Sunshine::ROOT}/templates/tasks/#{task}.rake"
501
- to = "#{ds.host}:#{path}/#{task}.rake"
520
+ use_remote_shell shell
502
521
 
503
- assert_rsync from, to
504
- end
522
+ assert_ssh_call "mkdir -p /path/to/tasks"
523
+
524
+ %w{common tpkg}.each do |task|
525
+ from = "#{Sunshine::ROOT}/templates/tasks/#{task}.rake"
526
+ to = "#{shell.host}:#{path}/#{task}.rake"
527
+
528
+ assert_rsync from, to
505
529
  end
506
530
  end
507
531
 
@@ -527,6 +551,21 @@ class TestApp < Test::Unit::TestCase
527
551
  end
528
552
 
529
553
 
554
+ def test_with_filter
555
+ app = Sunshine::App.new :repo => {:type => "svn", :url => @svn_url},
556
+ :remote_shells => ["user@server1.com", "user@server2.com"]
557
+
558
+ assert_equal 2, app.server_apps.length
559
+
560
+ app.with_filter :host => 'server1.com' do |app|
561
+ assert_equal 1, app.server_apps.length
562
+ assert_equal 'server1.com', app.server_apps.first.shell.host
563
+ end
564
+
565
+ assert_equal 2, app.server_apps.length
566
+ end
567
+
568
+
530
569
  def test_sudo_assignment
531
570
  @app.sudo = "someuser"
532
571
 
@@ -545,10 +584,8 @@ class TestApp < Test::Unit::TestCase
545
584
  assert_equal attr_hash[:root_path], app.root_path
546
585
 
547
586
  attr_hash[:remote_shells].each_with_index do |server_def, i|
548
- server_def = server_def.first if Array === server_def
549
- user, host = server_def.split("@")
550
- assert_equal host, app.server_apps[i].shell.host
551
- assert_equal user, app.server_apps[i].shell.user
587
+ shell = Sunshine::RemoteShell.new(*server_def)
588
+ assert_equal shell, app.server_apps[i].shell
552
589
  end
553
590
  end
554
591
 
@@ -10,27 +10,16 @@ class TestNginx < Test::Unit::TestCase
10
10
 
11
11
  @passenger = Sunshine::Nginx.new @app
12
12
  @nginx = Sunshine::Nginx.new @app, :port => 5000, :point_to => @passenger
13
- @gemout = <<-STR
14
13
 
15
- *** LOCAL GEMS ***
14
+ @passenger_root = "/Library/Ruby/Gems/1.8/gems/passenger-2.2.11"
16
15
 
17
- passenger (2.2.4)
18
- Author: Phusion - http://www.phusion.nl/
19
- Rubyforge: http://rubyforge.org/projects/passenger
20
- Homepage: http://www.modrails.com/
21
- Installed at: /Library/Ruby/Gems/1.8
22
-
23
- Apache module for Ruby on Rails support.
24
- STR
25
-
26
- @nginx_passenger_check =
27
- "/opt/ruby-ypc/lib/ruby/gems/1.8/gems/passenger-2.2.11/ext/nginx"
16
+ @nginx_passenger_check = "#{@passenger_root}/ext/nginx"
28
17
  end
29
18
 
30
19
 
31
20
  def test_cmd
32
21
  ds = @nginx.app.server_apps.first.shell
33
- ds.set_mock_response 0, "gem list passenger -d" => [:out, @gemout]
22
+ ds.set_mock_response 0, "passenger-config --root" => [:out, @passenger_root]
34
23
 
35
24
  @nginx.start
36
25
  @nginx.stop
@@ -42,7 +31,7 @@ passenger (2.2.4)
42
31
 
43
32
  def test_custom_sudo_cmd
44
33
  ds = @nginx.app.server_apps.first.shell
45
- ds.set_mock_response 0, "gem list passenger -d" => [:out, @gemout]
34
+ ds.set_mock_response 0, "passenger-config --root" => [:out, @passenger_root]
46
35
 
47
36
  @nginx.sudo = "someuser"
48
37
 
@@ -70,14 +59,13 @@ passenger (2.2.4)
70
59
  def test_setup_passenger
71
60
  ds = @passenger.app.server_apps.first.shell
72
61
 
73
- ds.set_mock_response 0,
74
- "gem list passenger -d" => [:out, @gemout],
75
- "nginx -V 2>&1" => [:out, @nginx_passenger_check]
62
+ ds.set_mock_response 0, "passenger-config --root" => [:out, @passenger_root]
63
+ ds.set_mock_response 0, "nginx -V 2>&1" => [:out, @nginx_passenger_check]
76
64
 
77
65
  @passenger.setup do |ds, binder|
78
66
  assert binder.sudo
79
67
  assert binder.use_passenger?
80
- assert_equal "/Library/Ruby/Gems/1.8/gems/passenger-2.2.4",
68
+ assert_equal "/Library/Ruby/Gems/1.8/gems/passenger-2.2.11",
81
69
  binder.passenger_root
82
70
  end
83
71
  end
@@ -85,12 +73,12 @@ passenger (2.2.4)
85
73
 
86
74
  def test_setup
87
75
  ds = @nginx.app.server_apps.first.shell
88
- ds.set_mock_response 0, "gem list passenger -d" => [:out, @gemout]
76
+ ds.set_mock_response 0, "passenger-config --root" => [:out, @passenger_root]
89
77
 
90
78
  @nginx.setup do |ds, binder|
91
79
  assert !binder.sudo
92
80
  assert !binder.use_passenger?
93
- assert_equal "/Library/Ruby/Gems/1.8/gems/passenger-2.2.4",
81
+ assert_equal "/Library/Ruby/Gems/1.8/gems/passenger-2.2.11",
94
82
  binder.passenger_root
95
83
  end
96
84
  end
@@ -231,7 +231,7 @@ class TestServer < Test::Unit::TestCase
231
231
 
232
232
  server.each_server_app do |sa|
233
233
  %w{start stop restart status}.each do |script|
234
- script_file = "#{server.config_path}/#{script}"
234
+ script_file = "#{@app.root_path}/env #{server.config_path}/#{script}"
235
235
  cmd = sa.shell.sudo_cmd script_file, server.send(:pick_sudo, sa.shell)
236
236
 
237
237
  assert sa.scripts[script.to_sym].include?(cmd.join(" "))
@@ -169,6 +169,23 @@ class TestServerApp < Test::Unit::TestCase
169
169
  end
170
170
 
171
171
 
172
+ def test_has_all_roles
173
+ assert @sa.has_roles?([:web, :app, :blarg])
174
+ assert @sa.has_roles?([:web, :app, :blarg], true)
175
+ end
176
+
177
+
178
+ def test_has_roles
179
+ @sa.roles = [:web, :app]
180
+
181
+ assert @sa.has_roles?(:web)
182
+ assert @sa.has_roles?([:web, :app])
183
+
184
+ assert !@sa.has_roles?([:blarg, :web, :app])
185
+ assert @sa.has_roles?([:blarg, :web, :app], true)
186
+ end
187
+
188
+
172
189
  def test_install_deps
173
190
  nginx_dep = Sunshine.dependencies.get 'nginx', :prefer => @sa.pkg_manager
174
191
 
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 1
7
7
  - 1
8
- - 1
9
- version: 1.1.1
8
+ - 2
9
+ version: 1.1.2
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-04-06 00:00:00 -07:00
17
+ date: 2010-04-15 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -108,9 +108,9 @@ files:
108
108
  - README.txt
109
109
  - Rakefile
110
110
  - bin/sunshine
111
- - examples/deploy.rb
112
111
  - examples/deploy_tasks.rake
113
112
  - examples/standalone_deploy.rb
113
+ - examples/sunshine_deploy.rb
114
114
  - lib/commands/add.rb
115
115
  - lib/commands/default.rb
116
116
  - lib/commands/list.rb