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

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.
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