sunshine 1.2.2 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,7 @@
1
1
  module Sunshine
2
2
 
3
3
  ##
4
- # Instantiated per deploy server and used to pass bindings to the config's
5
- # ERB build method:
4
+ # Create a selective binding. Useful for controlling ERB builds:
6
5
  # binder.set :server_name, "blah.com"
7
6
  # binder.forward :server_method, ...
8
7
  # binder.get_binding
@@ -14,7 +14,7 @@ module Sunshine
14
14
  START_FAILED_CODE = 10
15
15
  STOP_FAILED_CODE = 11
16
16
  RESTART_FAILED_CODE = 12
17
- STATUS_DOWN_CODE = 13
17
+ STATUS_DOWN_CODE = 13
18
18
 
19
19
  ##
20
20
  # Returns an array of method names to assign to the binder
@@ -14,7 +14,7 @@ module Sunshine
14
14
  def initialize app, options={}
15
15
  super
16
16
 
17
- @bin = options[:bin] || 'apachectl'
17
+ @bin = options[:bin] || 'apache2ctl'
18
18
 
19
19
  @sigkill = 'WINCH'
20
20
 
@@ -37,7 +37,7 @@ module Sunshine
37
37
  end
38
38
 
39
39
 
40
- attr_reader :host, :user, :pid
40
+ attr_reader :host, :user, :parent_pid
41
41
  attr_accessor :ssh_flags, :rsync_flags
42
42
 
43
43
 
@@ -69,7 +69,7 @@ module Sunshine
69
69
  @ssh_flags.concat ["-l", @user] if @user
70
70
  @ssh_flags.concat [*options[:ssh_flags]] if options[:ssh_flags]
71
71
 
72
- @pid, @inn, @out, @err = nil
72
+ @parent_pid = nil
73
73
 
74
74
  self.class.register self
75
75
  end
@@ -94,15 +94,15 @@ module Sunshine
94
94
 
95
95
  cmd = ssh_cmd quote_cmd(LOGIN_LOOP), :sudo => false
96
96
 
97
- @pid, @inn, @out, @err = popen4 cmd.join(" ")
98
- @inn.sync = true
97
+ @parent_pid, inn, out, err = popen4 cmd.join(" ")
98
+ inn.sync = true
99
99
 
100
100
  data = ""
101
101
  ready = nil
102
102
  start_time = Time.now.to_i
103
103
 
104
- until ready || @out.eof?
105
- data << @out.readpartial(1024)
104
+ until ready || out.eof?
105
+ data << out.readpartial(1024)
106
106
  ready = data =~ /ready/
107
107
 
108
108
  raise TimeoutError if timed_out?(start_time, LOGIN_TIMEOUT)
@@ -114,8 +114,11 @@ module Sunshine
114
114
  raise ConnectionError, "Can't connect to #{host_info}"
115
115
  end
116
116
 
117
- @inn.close
118
- @pid
117
+ inn.close rescue nil
118
+ out.close rescue nil
119
+ err.close rescue nil
120
+
121
+ @parent_pid
119
122
  end
120
123
 
121
124
 
@@ -123,7 +126,7 @@ module Sunshine
123
126
  # Check if SSH session is open and returns process pid
124
127
 
125
128
  def connected?
126
- Process.kill(0, @pid) && @pid rescue false
129
+ Process.kill(0, @parent_pid) && @parent_pid rescue false
127
130
  end
128
131
 
129
132
 
@@ -131,13 +134,8 @@ module Sunshine
131
134
  # Disconnect from host
132
135
 
133
136
  def disconnect
134
- @inn.close rescue nil
135
- @out.close rescue nil
136
- @err.close rescue nil
137
-
138
- kill_process @pid, "HUP" rescue nil
139
-
140
- @pid = nil
137
+ kill_process @parent_pid, "HUP" rescue nil
138
+ @parent_pid = nil
141
139
  end
142
140
 
143
141
 
@@ -100,7 +100,7 @@ module Sunshine
100
100
  end
101
101
 
102
102
 
103
- NAME_MATCH = /\/([^\/]+)\.git/
103
+ NAME_MATCH = /\/([^\/]+)(\/?)\.git/
104
104
 
105
105
  def name
106
106
  @url.match(NAME_MATCH)[1]
@@ -95,7 +95,7 @@ module Sunshine
95
95
  app_attr :root_path, :checkout_path, :current_path
96
96
  app_attr :deploys_path, :log_path, :shared_path, :scripts_path
97
97
 
98
- attr_accessor :app, :roles, :scripts, :info, :shell, :crontab, :health
98
+ attr_accessor :app, :roles, :scripts, :info, :shell, :crontab
99
99
  attr_writer :pkg_manager
100
100
 
101
101
  ##
@@ -131,7 +131,6 @@ module Sunshine
131
131
  end
132
132
 
133
133
  @crontab = Crontab.new name, @shell
134
- @health = Healthcheck.new shared_path, @shell
135
134
 
136
135
  @all_deploy_names = nil
137
136
  @previous_deploy_name = nil
@@ -180,7 +179,7 @@ module Sunshine
180
179
  write_script name, bash
181
180
  end
182
181
 
183
- symlink_scripts_to_root
182
+ symlink_scripts_to_root @scripts.keys, "env"
184
183
  end
185
184
 
186
185
 
@@ -222,6 +221,7 @@ module Sunshine
222
221
 
223
222
  def deploy_details reload=false
224
223
  return @deploy_details if @deploy_details && !reload
224
+
225
225
  @deploy_details =
226
226
  YAML.load @shell.call("cat #{self.root_path}/info") rescue nil
227
227
 
@@ -259,7 +259,7 @@ module Sunshine
259
259
  # Builds a hash with information about the deploy at hand.
260
260
 
261
261
  def get_deploy_info
262
- { :deployed_at => Time.now.to_s,
262
+ { :deployed_at => @shell.call("date"),
263
263
  :deployed_as => @shell.call("whoami"),
264
264
  :deployed_by => Sunshine.shell.user,
265
265
  :deploy_name => File.basename(self.checkout_path),
@@ -272,29 +272,6 @@ module Sunshine
272
272
  end
273
273
 
274
274
 
275
- ##
276
- # Decrypt a file using gpg. Allows options:
277
- # :output:: str - the path the output file should go to
278
- # :passphrase:: str - the passphrase gpg should use
279
-
280
- def gpg_decrypt gpg_file, options={}
281
- output_file = options[:output] || gpg_file.gsub(/\.gpg$/, '')
282
-
283
- passphrase = options[:passphrase]
284
- passphrase_file = "#{self.root_path}/tmp/gpg_passphrase"
285
-
286
- gpg_cmd = "gpg --batch --no-tty --yes --output #{output_file} "+
287
- "--passphrase-file #{passphrase_file} --decrypt #{gpg_file}"
288
-
289
- @shell.call "mkdir -p #{File.dirname(passphrase_file)}"
290
-
291
- @shell.make_file passphrase_file, passphrase
292
-
293
- @shell.call "cd #{self.checkout_path} && #{gpg_cmd}"
294
- @shell.call "rm -f #{passphrase_file}"
295
- end
296
-
297
-
298
275
  ##
299
276
  # Check if this server app includes the specified roles:
300
277
  # server_app.has_roles? :web
@@ -418,7 +395,7 @@ fi
418
395
  # Adds the app to the deploy server's deployed-apps list
419
396
 
420
397
  def register_as_deployed
421
- AddCommand.exec self.root_path, 'servers' => [@shell]
398
+ AddCommand.exec "#{self.name}:#{self.root_path}", 'servers' => [@shell]
422
399
  end
423
400
 
424
401
 
@@ -523,7 +500,7 @@ fi
523
500
  def run_script! name, options=nil, &block
524
501
  options ||= {}
525
502
 
526
- script_path = File.join self.root_path, name.to_s
503
+ script_path = File.join self.scripts_path, name.to_s
527
504
  @shell.call script_path, options, &block
528
505
  end
529
506
 
@@ -638,15 +615,13 @@ fi
638
615
 
639
616
 
640
617
  ##
641
- # Creates a symlink of every script in the scripts_path dir in the
618
+ # Creates a symlink of every script_name from the scripts_path dir to the
642
619
  # app's root directory for easy access.
643
620
 
644
- def symlink_scripts_to_root
645
- scripts = @shell.call("ls -1 #{self.scripts_path}").split("\n")
646
-
647
- scripts.each do |name|
648
- script_file = File.join self.scripts_path, name
649
- pointer_file = File.join self.root_path, name
621
+ def symlink_scripts_to_root *script_names
622
+ script_names.flatten.each do |name, val|
623
+ script_file = File.join self.scripts_path, name.to_s
624
+ pointer_file = File.join self.root_path, name.to_s
650
625
 
651
626
  @shell.symlink script_file, pointer_file
652
627
  end
@@ -669,45 +644,6 @@ fi
669
644
  end
670
645
 
671
646
 
672
- ##
673
- # Upload common rake tasks from a local path or the sunshine lib.
674
- # app.upload_tasks
675
- # #=> upload all tasks
676
- # app.upload_tasks 'app', 'common', ...
677
- # #=> upload app and common rake files
678
- #
679
- # File paths may also be used instead of the file's base name but
680
- # directory structures will not be followed:
681
- # app.upload_tasks 'lib/common/app.rake', 'lib/do_thing.rake'
682
- #
683
- # Allows options:
684
- # :local_path:: str - the path to get rake tasks from
685
- # :remote_path:: str - the remote absolute path to upload the files to
686
-
687
- def upload_tasks *files
688
- options = Hash === files[-1] ? files.delete_at(-1) : {}
689
- remote_path = options[:remote_path] || "#{self.checkout_path}/lib/tasks"
690
- local_path = options[:local_path] || "#{Sunshine::ROOT}/templates/tasks"
691
-
692
- @shell.call "mkdir -p #{remote_path}"
693
-
694
- files.map! do |file|
695
- if File.basename(file) == file
696
- File.join(local_path, "#{file}.rake")
697
- else
698
- file
699
- end
700
- end
701
-
702
- files = Dir.glob("#{Sunshine::ROOT}/templates/tasks/*") if files.empty?
703
-
704
- files.each do |file|
705
- remote_file = File.join remote_path, File.basename(file)
706
- @shell.upload file, remote_file
707
- end
708
- end
709
-
710
-
711
647
  ##
712
648
  # Write an executable bash script to the app's scripts dir
713
649
  # on the deploy server, and symlink them to the root dir.
@@ -738,7 +674,7 @@ fi
738
674
  @shared_path = "#{@root_path}/shared"
739
675
  @log_path = "#{@shared_path}/log"
740
676
  @checkout_path = "#{@deploys_path}/#{@deploy_name}"
741
- @scripts_path = "#{@checkout_path}/sunshine_scripts"
677
+ @scripts_path = "#{@current_path}/script"
742
678
  end
743
679
  end
744
680
  end
@@ -25,7 +25,7 @@ module Sunshine
25
25
  self.sudo_failed_matcher = /^Sorry, try again./
26
26
  self.sudo_prompt_matcher = /^Password:/
27
27
 
28
- attr_reader :user, :host, :password, :input, :output, :mutex
28
+ attr_reader :user, :host, :password, :input, :output, :mutex, :pid
29
29
  attr_accessor :env, :sudo, :timeout
30
30
 
31
31
  def initialize output = $stdout, options={}
@@ -42,8 +42,9 @@ module Sunshine
42
42
  @password = options[:password]
43
43
 
44
44
  @timeout = options[:timeout] || Sunshine.timeout
45
+ @idle_time = options[:idle_after] || 1
45
46
 
46
- @mutex = nil
47
+ @mutex = @pid = nil
47
48
  end
48
49
 
49
50
 
@@ -149,6 +150,31 @@ module Sunshine
149
150
  end
150
151
 
151
152
 
153
+ ##
154
+ # Checks if timeout occurred.
155
+
156
+ def timed_out? start_time=@cmd_activity, max_time=@timeout
157
+ return unless max_time
158
+ Time.now.to_f - start_time.to_f > max_time
159
+ end
160
+
161
+
162
+ ##
163
+ # Update the time of the last command activity
164
+
165
+ def update_activity
166
+ @cmd_activity = Time.now
167
+ end
168
+
169
+
170
+ ##
171
+ # Checks if shell is still receiving data.
172
+
173
+ def idle? start_time=@cmd_activity, max_time=@idle_time
174
+ timed_out? start_time, max_time
175
+ end
176
+
177
+
152
178
  ##
153
179
  # Start an interactive shell with preset permissions and env.
154
180
  # Optionally pass a command to be run first.
@@ -273,15 +299,6 @@ module Sunshine
273
299
  end
274
300
 
275
301
 
276
- ##
277
- # Checks if timeout occurred.
278
-
279
- def timed_out? start_time, max_time=@timeout
280
- return unless max_time
281
- Time.now.to_i - start_time.to_i > max_time
282
- end
283
-
284
-
285
302
  ##
286
303
  # Execute a block while setting the shell's mutex.
287
304
  # Sets the mutex to its original value on exit.
@@ -336,29 +353,28 @@ module Sunshine
336
353
 
337
354
  def execute cmd
338
355
  cmd = [cmd] unless Array === cmd
339
- pid, inn, out, err = popen4(*cmd)
356
+ @pid, inn, out, err = popen4(*cmd)
340
357
 
341
358
  inn.sync = true
342
359
  log_methods = {out => :debug, err => :error}
343
360
 
344
- result, status = process_streams(pid, out, err) do |stream, data|
361
+ result, status = process_streams(@pid, out, err) do |stream, data|
345
362
  stream_name = :out if stream == out
346
363
  stream_name = :err if stream == err
347
364
  stream_name = :inn if stream == inn
348
365
 
366
+ Sunshine.logger.send log_methods[stream],
367
+ "#{@host}:#{stream_name}", data
349
368
 
350
369
  # User blocks should run with sync threads to avoid badness.
351
370
  sync do
352
- Sunshine.logger.send log_methods[stream],
353
- "#{@host}:#{stream_name}", data
354
-
355
371
  yield(stream_name, data, inn) if block_given?
356
372
  end
357
373
 
358
374
 
359
375
  if password_required?(stream_name, data) then
360
376
 
361
- kill_process(pid) unless Sunshine.interactive?
377
+ kill_process(@pid) unless Sunshine.interactive?
362
378
 
363
379
  send_password_to_stream(inn, data)
364
380
  end
@@ -372,6 +388,7 @@ module Sunshine
372
388
  inn.close rescue nil
373
389
  out.close rescue nil
374
390
  err.close rescue nil
391
+ @pid = nil
375
392
  end
376
393
 
377
394
 
@@ -405,7 +422,7 @@ module Sunshine
405
422
 
406
423
  def process_streams pid, *streams
407
424
  result = Hash.new{|h,k| h[k] = []}
408
- start_time = Time.now
425
+ update_activity
409
426
 
410
427
  # Handle process termination ourselves
411
428
  status = nil
@@ -417,13 +434,14 @@ module Sunshine
417
434
  # don't busy loop
418
435
  selected, = select streams, nil, nil, 0.1
419
436
 
420
- raise TimeoutError if timed_out? start_time
437
+ puts "#{@host} IDLE..." if idle?
438
+ raise TimeoutError if timed_out?
421
439
 
422
440
  next if selected.nil? or selected.empty?
423
441
 
424
442
  selected.each do |stream|
425
443
 
426
- start_time = Time.now
444
+ update_activity
427
445
 
428
446
  if stream.eof? then
429
447
  streams.delete stream if status # we've quit, so no more writing
@@ -1,4 +1,3 @@
1
- LoadModule log_config_module modules/mod_log_config.so
2
1
  LoadModule authz_host_module modules/mod_authz_host.so
3
2
 
4
3
  ErrorLog <%= expand_path log_file(:stderr) %>
@@ -25,7 +24,6 @@ MaxClients <%= connections %>
25
24
 
26
25
  <% if sudo == true || sudo == 'root' -%>
27
26
  User nobody
28
- Group nobody
29
27
  <% end -%>
30
28
 
31
29
  Listen <%= port %>
@@ -92,12 +92,6 @@ namespace :sunshine do
92
92
 
93
93
  # Post-deploy control tasks:
94
94
 
95
- desc "Run db:migrate on remote :db servers"
96
- task :db_migrate => :app do
97
- @app.rake 'db:migrate', :role => :db
98
- end
99
-
100
-
101
95
  desc "Run the remote start script"
102
96
  task :start => :app do
103
97
  @app.start
@@ -129,31 +123,4 @@ namespace :sunshine do
129
123
  task :info => :app do
130
124
  puts @app.deploy_details.to_yaml
131
125
  end
132
-
133
-
134
- desc "Get the health state"
135
- task :health => :app do
136
- puts @app.health.to_yaml
137
- end
138
-
139
-
140
- namespace :health do
141
-
142
- desc "Turn on health check"
143
- task :enable => :app do
144
- puts @app.health(:enable).to_yaml
145
- end
146
-
147
-
148
- desc "Turn off health check"
149
- task :disable => :app do
150
- puts @app.health(:disable).to_yaml
151
- end
152
-
153
-
154
- desc "Remove health check"
155
- task :remove => :app do
156
- puts @app.health(:remove).to_yaml
157
- end
158
- end
159
126
  end