sunshine 1.2.2 → 1.2.3
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.
- 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
|