engineyard-serverside 1.5.35.pre.2 → 1.6.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/lib/engineyard-serverside.rb +3 -1
  2. data/lib/engineyard-serverside/cli.rb +73 -38
  3. data/lib/engineyard-serverside/configuration.rb +38 -12
  4. data/lib/engineyard-serverside/deploy.rb +63 -51
  5. data/lib/engineyard-serverside/deploy_hook.rb +21 -18
  6. data/lib/engineyard-serverside/deprecation.rb +9 -17
  7. data/lib/engineyard-serverside/lockfile_parser.rb +1 -1
  8. data/lib/engineyard-serverside/rails_asset_support.rb +5 -5
  9. data/lib/engineyard-serverside/server.rb +8 -11
  10. data/lib/engineyard-serverside/shell.rb +101 -0
  11. data/lib/engineyard-serverside/shell/formatter.rb +70 -0
  12. data/lib/engineyard-serverside/shell/helpers.rb +29 -0
  13. data/lib/engineyard-serverside/strategies/git.rb +12 -15
  14. data/lib/engineyard-serverside/task.rb +28 -5
  15. data/lib/engineyard-serverside/version.rb +1 -1
  16. data/lib/vendor/open4/lib/open4.rb +432 -0
  17. data/spec/basic_deploy_spec.rb +9 -9
  18. data/spec/bundler_deploy_spec.rb +1 -1
  19. data/spec/custom_deploy_spec.rb +45 -4
  20. data/spec/deploy_hook_spec.rb +77 -78
  21. data/spec/deprecation_spec.rb +4 -26
  22. data/spec/git_strategy_spec.rb +6 -2
  23. data/spec/nodejs_deploy_spec.rb +2 -2
  24. data/spec/services_deploy_spec.rb +11 -10
  25. data/spec/shell_spec.rb +48 -0
  26. data/spec/spec_helper.rb +48 -25
  27. data/spec/sqlite3_deploy_spec.rb +1 -2
  28. data/spec/support/integration.rb +1 -13
  29. metadata +57 -97
  30. data/lib/engineyard-serverside/logged_output.rb +0 -91
  31. data/lib/vendor/systemu/LICENSE +0 -3
  32. data/lib/vendor/systemu/lib/systemu.rb +0 -363
  33. data/lib/vendor/systemu/systemu.gemspec +0 -45
  34. data/spec/fixtures/gitrepo/bar +0 -0
  35. data/spec/logged_output_spec.rb +0 -55
@@ -1,12 +1,16 @@
1
+ require 'engineyard-serverside/shell/helpers'
2
+
1
3
  module EY
2
4
  module Serverside
3
5
  class Task
6
+ include EY::Serverside::Shell::Helpers
4
7
 
5
- attr_reader :config
8
+ attr_reader :config, :shell
6
9
  alias :c :config
7
10
 
8
- def initialize(conf)
11
+ def initialize(conf, shell = nil)
9
12
  @config = conf
13
+ @shell = shell
10
14
  @roles = :all
11
15
  end
12
16
 
@@ -18,7 +22,7 @@ module EY
18
22
  end
19
23
 
20
24
  if deploy_file
21
- puts "~> Loading deployment task overrides from #{deploy_file}"
25
+ shell.status "Loading deployment task overrides from #{deploy_file}"
22
26
  instance_eval(File.read(deploy_file))
23
27
  true
24
28
  else
@@ -26,6 +30,25 @@ module EY
26
30
  end
27
31
  end
28
32
 
33
+ def load_ey_yml
34
+ ey_yml = ["config/ey.yml", "ey.yml"].map do |short_file|
35
+ File.join(c.repository_cache, short_file)
36
+ end.detect do |file|
37
+ File.exist?(file)
38
+ end
39
+
40
+ if ey_yml
41
+ shell.status "Loading deploy configuration in #{ey_yml}"
42
+ data = YAML.load_file(ey_yml)
43
+ config.ey_yml_data = data
44
+ else
45
+ false
46
+ end
47
+ rescue Exception
48
+ shell.error "Error loading YAML in #{ey_yml}"
49
+ raise
50
+ end
51
+
29
52
  def roles(*task_roles)
30
53
  raise "Roles must be passed a block" unless block_given?
31
54
 
@@ -50,11 +73,11 @@ module EY
50
73
 
51
74
  private
52
75
 
53
- def run_on_roles(cmd, &block)
76
+ def run_on_roles(cmd, wrapper=%w[sh -l -c], &block)
54
77
  servers = EY::Serverside::Server.from_roles(@roles)
55
78
  futures = EY::Serverside::Future.call(servers, block_given?) do |server, exec_block|
56
79
  to_run = exec_block ? block.call(server, cmd.dup) : cmd
57
- server.run(to_run)
80
+ server.run(Escape.shell_command(wrapper + [to_run])) { |cmd| shell.logged_system(cmd) }
58
81
  end
59
82
 
60
83
  unless EY::Serverside::Future.success?(futures)
@@ -1,5 +1,5 @@
1
1
  module EY
2
2
  module Serverside
3
- VERSION = '1.5.35.pre.2'
3
+ VERSION = '1.6.0.pre'
4
4
  end
5
5
  end
@@ -0,0 +1,432 @@
1
+ # vim: ts=2:sw=2:sts=2:et:fdm=marker
2
+ require 'fcntl'
3
+ require 'timeout'
4
+ require 'thread'
5
+
6
+ module Open4
7
+ VERSION = '1.3.0'
8
+ def self.version() VERSION end
9
+
10
+ class Error < ::StandardError; end
11
+
12
+ def pfork4(fun, &b)
13
+ Open4.do_popen(b, :block) do |ps_read, _|
14
+ ps_read.close
15
+ begin
16
+ fun.call
17
+ rescue SystemExit => e
18
+ # Make it seem to the caller that calling Kernel#exit in +fun+ kills
19
+ # the child process normally. Kernel#exit! bypasses this rescue
20
+ # block.
21
+ exit! e.status
22
+ else
23
+ exit! 0
24
+ end
25
+ end
26
+ end
27
+ module_function :pfork4
28
+
29
+ def popen4(*cmd, &b)
30
+ Open4.do_popen(b, :init) do |ps_read, ps_write|
31
+ ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
32
+ ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
33
+ exec(*cmd)
34
+ raise 'forty-two' # Is this really needed?
35
+ end
36
+ end
37
+ alias open4 popen4
38
+ module_function :popen4
39
+ module_function :open4
40
+
41
+ def popen4ext(closefds=false, *cmd, &b)
42
+ Open4.do_popen(b, :init, closefds) do |ps_read, ps_write|
43
+ ps_read.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
44
+ ps_write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
45
+ exec(*cmd)
46
+ raise 'forty-two' # Is this really needed?
47
+ end
48
+ end
49
+ module_function :popen4ext
50
+
51
+ def self.do_popen(b = nil, exception_propagation_at = nil, closefds=false, &cmd)
52
+ pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
53
+
54
+ verbose = $VERBOSE
55
+ begin
56
+ $VERBOSE = nil
57
+
58
+ cid = fork {
59
+ if closefds
60
+ exlist = [0, 1, 2] | [pw,pr,pe,ps].map{|p| [p.first.fileno, p.last.fileno] }.flatten
61
+ ObjectSpace.each_object(IO){|io|
62
+ io.close if (not io.closed?) and (not exlist.include? io.fileno)
63
+ }
64
+ end
65
+
66
+ pw.last.close
67
+ STDIN.reopen pw.first
68
+ pw.first.close
69
+
70
+ pr.first.close
71
+ STDOUT.reopen pr.last
72
+ pr.last.close
73
+
74
+ pe.first.close
75
+ STDERR.reopen pe.last
76
+ pe.last.close
77
+
78
+ STDOUT.sync = STDERR.sync = true
79
+
80
+ begin
81
+ cmd.call(ps)
82
+ rescue Exception => e
83
+ Marshal.dump(e, ps.last)
84
+ ps.last.flush
85
+ ensure
86
+ ps.last.close unless ps.last.closed?
87
+ end
88
+
89
+ exit!
90
+ }
91
+ ensure
92
+ $VERBOSE = verbose
93
+ end
94
+
95
+ [ pw.first, pr.last, pe.last, ps.last ].each { |fd| fd.close }
96
+
97
+ Open4.propagate_exception cid, ps.first if exception_propagation_at == :init
98
+
99
+ pw.last.sync = true
100
+
101
+ pi = [ pw.last, pr.first, pe.first ]
102
+
103
+ begin
104
+ return [cid, *pi] unless b
105
+
106
+ begin
107
+ b.call(cid, *pi)
108
+ ensure
109
+ pi.each { |fd| fd.close unless fd.closed? }
110
+ end
111
+
112
+ Open4.propagate_exception cid, ps.first if exception_propagation_at == :block
113
+
114
+ Process.waitpid2(cid).last
115
+ ensure
116
+ ps.first.close unless ps.first.closed?
117
+ end
118
+ end
119
+
120
+ def self.propagate_exception(cid, ps_read)
121
+ e = Marshal.load ps_read
122
+ raise Exception === e ? e : "unknown failure!"
123
+ rescue EOFError
124
+ # Child process did not raise exception.
125
+ rescue
126
+ # Child process raised exception; wait it in order to avoid a zombie.
127
+ Process.waitpid2 cid
128
+ raise
129
+ ensure
130
+ ps_read.close
131
+ end
132
+
133
+ class SpawnError < Error
134
+ attr 'cmd'
135
+ attr 'status'
136
+ attr 'signals'
137
+ def exitstatus
138
+ @status.exitstatus
139
+ end
140
+ def initialize cmd, status
141
+ @cmd, @status = cmd, status
142
+ @signals = {}
143
+ if status.signaled?
144
+ @signals['termsig'] = status.termsig
145
+ @signals['stopsig'] = status.stopsig
146
+ end
147
+ sigs = @signals.map{|k,v| "#{ k }:#{ v.inspect }"}.join(' ')
148
+ super "cmd <#{ cmd }> failed with status <#{ exitstatus.inspect }> signals <#{ sigs }>"
149
+ end
150
+ end
151
+
152
+ class ThreadEnsemble
153
+ attr 'threads'
154
+
155
+ def initialize cid
156
+ @cid, @threads, @argv, @done, @running = cid, [], [], Queue.new, false
157
+ @killed = false
158
+ end
159
+
160
+ def add_thread *a, &b
161
+ @running ? raise : (@argv << [a, b])
162
+ end
163
+
164
+ #
165
+ # take down process more nicely
166
+ #
167
+ def killall
168
+ c = Thread.critical
169
+ return nil if @killed
170
+ Thread.critical = true
171
+ (@threads - [Thread.current]).each{|t| t.kill rescue nil}
172
+ @killed = true
173
+ ensure
174
+ Thread.critical = c
175
+ end
176
+
177
+ def run
178
+ @running = true
179
+
180
+ begin
181
+ @argv.each do |a, b|
182
+ @threads << Thread.new(*a) do |*a|
183
+ begin
184
+ b[*a]
185
+ ensure
186
+ killall rescue nil if $!
187
+ @done.push Thread.current
188
+ end
189
+ end
190
+ end
191
+ rescue
192
+ killall
193
+ raise
194
+ ensure
195
+ all_done
196
+ end
197
+
198
+ @threads.map{|t| t.value}
199
+ end
200
+
201
+ def all_done
202
+ @threads.size.times{ @done.pop }
203
+ end
204
+ end
205
+
206
+ def to timeout = nil
207
+ Timeout.timeout(timeout){ yield }
208
+ end
209
+ module_function :to
210
+
211
+ def new_thread *a, &b
212
+ cur = Thread.current
213
+ Thread.new(*a) do |*a|
214
+ begin
215
+ b[*a]
216
+ rescue Exception => e
217
+ cur.raise e
218
+ end
219
+ end
220
+ end
221
+ module_function :new_thread
222
+
223
+ def getopts opts = {}
224
+ lambda do |*args|
225
+ keys, default, ignored = args
226
+ catch(:opt) do
227
+ [keys].flatten.each do |key|
228
+ [key, key.to_s, key.to_s.intern].each do |key|
229
+ throw :opt, opts[key] if opts.has_key?(key)
230
+ end
231
+ end
232
+ default
233
+ end
234
+ end
235
+ end
236
+ module_function :getopts
237
+
238
+ def relay src, dst = nil, t = nil
239
+ send_dst =
240
+ if dst.respond_to?(:call)
241
+ lambda{|buf| dst.call(buf)}
242
+ elsif dst.respond_to?(:<<)
243
+ lambda{|buf| dst << buf }
244
+ else
245
+ lambda{|buf| buf }
246
+ end
247
+
248
+ unless src.nil?
249
+ if src.respond_to? :gets
250
+ while buf = to(t){ src.gets }
251
+ send_dst[buf]
252
+ end
253
+
254
+ elsif src.respond_to? :each
255
+ q = Queue.new
256
+ th = nil
257
+
258
+ timer_set = lambda do |t|
259
+ th = new_thread{ to(t){ q.pop } }
260
+ end
261
+
262
+ timer_cancel = lambda do |t|
263
+ th.kill if th rescue nil
264
+ end
265
+
266
+ timer_set[t]
267
+ begin
268
+ src.each do |buf|
269
+ timer_cancel[t]
270
+ send_dst[buf]
271
+ timer_set[t]
272
+ end
273
+ ensure
274
+ timer_cancel[t]
275
+ end
276
+
277
+ elsif src.respond_to? :read
278
+ buf = to(t){ src.read }
279
+ send_dst[buf]
280
+
281
+ else
282
+ buf = to(t){ src.to_s }
283
+ send_dst[buf]
284
+ end
285
+ end
286
+ end
287
+ module_function :relay
288
+
289
+ def spawn arg, *argv
290
+ argv.unshift(arg)
291
+ opts = ((argv.size > 1 and Hash === argv.last) ? argv.pop : {})
292
+ argv.flatten!
293
+ cmd = argv.join(' ')
294
+
295
+
296
+ getopt = getopts opts
297
+
298
+ ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ]
299
+ ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ]
300
+ exitstatus = getopt[ %w( exitstatus exit_status status ) ]
301
+ stdin = getopt[ %w( stdin in i 0 ) << 0 ]
302
+ stdout = getopt[ %w( stdout out o 1 ) << 1 ]
303
+ stderr = getopt[ %w( stderr err e 2 ) << 2 ]
304
+ pid = getopt[ 'pid' ]
305
+ timeout = getopt[ %w( timeout spawn_timeout ) ]
306
+ stdin_timeout = getopt[ %w( stdin_timeout ) ]
307
+ stdout_timeout = getopt[ %w( stdout_timeout io_timeout ) ]
308
+ stderr_timeout = getopt[ %w( stderr_timeout ) ]
309
+ status = getopt[ %w( status ) ]
310
+ cwd = getopt[ %w( cwd dir ) ]
311
+
312
+ exitstatus =
313
+ case exitstatus
314
+ when TrueClass, FalseClass
315
+ ignore_exit_failure = true if exitstatus
316
+ [0]
317
+ else
318
+ [*(exitstatus || 0)].map{|i| Integer i}
319
+ end
320
+
321
+ stdin ||= '' if stdin_timeout
322
+ stdout ||= '' if stdout_timeout
323
+ stderr ||= '' if stderr_timeout
324
+
325
+ started = false
326
+
327
+ status =
328
+ begin
329
+ chdir(cwd) do
330
+ Timeout::timeout(timeout) do
331
+ popen4(*argv) do |c, i, o, e|
332
+ started = true
333
+
334
+ %w( replace pid= << push update ).each do |msg|
335
+ break(pid.send(msg, c)) if pid.respond_to? msg
336
+ end
337
+
338
+ te = ThreadEnsemble.new c
339
+
340
+ te.add_thread(i, stdin) do |i, stdin|
341
+ relay stdin, i, stdin_timeout
342
+ i.close rescue nil
343
+ end
344
+
345
+ te.add_thread(o, stdout) do |o, stdout|
346
+ relay o, stdout, stdout_timeout
347
+ end
348
+
349
+ te.add_thread(e, stderr) do |o, stderr|
350
+ relay e, stderr, stderr_timeout
351
+ end
352
+
353
+ te.run
354
+ end
355
+ end
356
+ end
357
+ rescue
358
+ raise unless(not started and ignore_exec_failure)
359
+ end
360
+
361
+ raise SpawnError.new(cmd, status) unless
362
+ (ignore_exit_failure or (status.nil? and ignore_exec_failure) or exitstatus.include?(status.exitstatus))
363
+
364
+ status
365
+ end
366
+ module_function :spawn
367
+
368
+ def chdir cwd, &block
369
+ return(block.call Dir.pwd) unless cwd
370
+ Dir.chdir cwd, &block
371
+ end
372
+ module_function :chdir
373
+
374
+ def background arg, *argv
375
+ require 'thread'
376
+ q = Queue.new
377
+ opts = { 'pid' => q, :pid => q }
378
+ case argv.last
379
+ when Hash
380
+ argv.last.update opts
381
+ else
382
+ argv.push opts
383
+ end
384
+ thread = Thread.new(arg, argv){|arg, argv| spawn arg, *argv}
385
+ sc = class << thread; self; end
386
+ sc.module_eval {
387
+ define_method(:pid){ @pid ||= q.pop }
388
+ define_method(:spawn_status){ @spawn_status ||= value }
389
+ define_method(:exitstatus){ @exitstatus ||= spawn_status.exitstatus }
390
+ }
391
+ thread
392
+ end
393
+ alias bg background
394
+ module_function :background
395
+ module_function :bg
396
+
397
+ def maim pid, opts = {}
398
+ getopt = getopts opts
399
+ sigs = getopt[ 'signals', %w(SIGTERM SIGQUIT SIGKILL) ]
400
+ suspend = getopt[ 'suspend', 4 ]
401
+ pid = Integer pid
402
+ existed = false
403
+ sigs.each do |sig|
404
+ begin
405
+ Process.kill sig, pid
406
+ existed = true
407
+ rescue Errno::ESRCH
408
+ return(existed ? nil : true)
409
+ end
410
+ return true unless alive? pid
411
+ sleep suspend
412
+ return true unless alive? pid
413
+ end
414
+ return(not alive?(pid))
415
+ end
416
+ module_function :maim
417
+
418
+ def alive pid
419
+ pid = Integer pid
420
+ begin
421
+ Process.kill 0, pid
422
+ true
423
+ rescue Errno::ESRCH
424
+ false
425
+ end
426
+ end
427
+ alias alive? alive
428
+ module_function :alive
429
+ module_function :'alive?'
430
+ end
431
+
432
+ def open4(*cmd, &b) cmd.size == 0 ? Open4 : Open4::popen4(*cmd, &b) end