sunshine 1.2.2 → 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +14 -0
- data/Manifest.txt +0 -3
- data/README.txt +50 -43
- data/Rakefile +4 -4
- data/bin/sunshine +1 -1
- data/examples/deploy_tasks.rake +0 -27
- data/lib/commands/list.rb +0 -24
- data/lib/sunshine.rb +24 -14
- data/lib/sunshine/app.rb +127 -211
- data/lib/sunshine/binder.rb +1 -2
- data/lib/sunshine/daemon.rb +1 -1
- data/lib/sunshine/daemons/apache.rb +1 -1
- data/lib/sunshine/remote_shell.rb +14 -16
- data/lib/sunshine/repos/git_repo.rb +1 -1
- data/lib/sunshine/server_app.rb +12 -76
- data/lib/sunshine/shell.rb +38 -20
- data/templates/apache/apache.conf.erb +0 -2
- data/templates/sunshine/sunshine.rake +0 -33
- data/test/unit/test_app.rb +0 -43
- data/test/unit/test_nginx.rb +1 -1
- data/test/unit/test_server_app.rb +17 -87
- metadata +20 -32
- data/lib/sunshine/healthcheck.rb +0 -98
- data/templates/sunshine/middleware/health.rb +0 -58
- data/test/unit/test_healthcheck.rb +0 -70
data/lib/sunshine/binder.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
module Sunshine
|
2
2
|
|
3
3
|
##
|
4
|
-
#
|
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
|
data/lib/sunshine/daemon.rb
CHANGED
@@ -37,7 +37,7 @@ module Sunshine
|
|
37
37
|
end
|
38
38
|
|
39
39
|
|
40
|
-
attr_reader :host, :user, :
|
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
|
-
@
|
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
|
-
@
|
98
|
-
|
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 ||
|
105
|
-
data <<
|
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
|
-
|
118
|
-
|
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, @
|
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
|
-
@
|
135
|
-
@
|
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
|
|
data/lib/sunshine/server_app.rb
CHANGED
@@ -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
|
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 =>
|
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.
|
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
|
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
|
-
|
646
|
-
|
647
|
-
|
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 = "#{@
|
677
|
+
@scripts_path = "#{@current_path}/script"
|
742
678
|
end
|
743
679
|
end
|
744
680
|
end
|
data/lib/sunshine/shell.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|