foreman 0.61.0 → 0.62.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: afe8eb788ae1b342ed0704a9e0504eee90c40549
4
+ data.tar.gz: 4493993e3ed2b5eeeffe5ae3c0d9c9bac78de32a
5
+ SHA512:
6
+ metadata.gz: 89be93397c4210acae5b5e0d258e50546d47727edc1cc8fc4908746b6bcb58bcc50e966244d5820fcceb5f2630896317151664c4a52680779b5f96d206950cd6
7
+ data.tar.gz: da1fc2ca548f01540e0f391841934c8cb7b8a499a18ca42c951270f295b9112685191cc2332a74074b5242ae3c8a10df8f72a2224419d20ad6afd49998d1bc22
@@ -23,7 +23,7 @@ class Foreman::CLI < Thor
23
23
  method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
24
24
  method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"'
25
25
  method_option :port, :type => :numeric, :aliases => "-p"
26
- method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shudown gracefully before receiving a SIGKILL, defaults to 5."
26
+ method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5."
27
27
 
28
28
  class << self
29
29
  # Hackery. Take the run method away from Thor so that we can redefine it.
@@ -9,6 +9,10 @@ require "thread"
9
9
 
10
10
  class Foreman::Engine
11
11
 
12
+ # The signals that the engine cares about.
13
+ #
14
+ HANDLED_SIGNALS = [ :TERM, :INT, :HUP ]
15
+
12
16
  attr_reader :env
13
17
  attr_reader :options
14
18
  attr_reader :processes
@@ -33,6 +37,16 @@ class Foreman::Engine
33
37
  @processes = []
34
38
  @running = {}
35
39
  @readers = {}
40
+
41
+ # Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html)
42
+ reader, writer = create_pipe
43
+ reader.close_on_exec = true if reader.respond_to?(:close_on_exec)
44
+ writer.close_on_exec = true if writer.respond_to?(:close_on_exec)
45
+ @selfpipe = { :reader => reader, :writer => writer }
46
+
47
+ # Set up a global signal queue
48
+ # http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html
49
+ Thread.main[:signal_queue] = []
36
50
  end
37
51
 
38
52
  # Start the processes registered to this +Engine+
@@ -41,10 +55,7 @@ class Foreman::Engine
41
55
  # Make sure foreman is the process group leader.
42
56
  Process.setpgrp unless Foreman.windows?
43
57
 
44
- trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
45
- trap("INT") { puts "SIGINT received"; terminate_gracefully }
46
- trap("HUP") { puts "SIGHUP received"; terminate_gracefully } if ::Signal.list.keys.include? 'HUP'
47
-
58
+ register_signal_handlers
48
59
  startup
49
60
  spawn_processes
50
61
  watch_for_output
@@ -53,6 +64,74 @@ class Foreman::Engine
53
64
  shutdown
54
65
  end
55
66
 
67
+ # Set up deferred signal handlers
68
+ #
69
+ def register_signal_handlers
70
+ HANDLED_SIGNALS.each do |sig|
71
+ if ::Signal.list.include? sig.to_s
72
+ trap(sig) { Thread.main[:signal_queue] << sig ; notice_signal }
73
+ end
74
+ end
75
+ end
76
+
77
+ # Unregister deferred signal handlers
78
+ #
79
+ def restore_default_signal_handlers
80
+ HANDLED_SIGNALS.each do |sig|
81
+ trap(sig, :DEFAULT) if ::Signal.list.include? sig.to_s
82
+ end
83
+ end
84
+
85
+ # Wake the main thread up via the selfpipe when there's a signal
86
+ #
87
+ def notice_signal
88
+ @selfpipe[:writer].write_nonblock( '.' )
89
+ rescue Errno::EAGAIN
90
+ # Ignore writes that would block
91
+ rescue Errno::EINT
92
+ # Retry if another signal arrived while writing
93
+ retry
94
+ end
95
+
96
+ # Invoke the real handler for signal +sig+. This shouldn't be called directly
97
+ # by signal handlers, as it might invoke code which isn't re-entrant.
98
+ #
99
+ # @param [Symbol] sig the name of the signal to be handled
100
+ #
101
+ def handle_signal(sig)
102
+ case sig
103
+ when :TERM
104
+ handle_term_signal
105
+ when :INT
106
+ handle_interrupt
107
+ when :HUP
108
+ handle_hangup
109
+ else
110
+ system "unhandled signal #{sig}"
111
+ end
112
+ end
113
+
114
+ # Handle a TERM signal
115
+ #
116
+ def handle_term_signal
117
+ puts "SIGTERM received"
118
+ terminate_gracefully
119
+ end
120
+
121
+ # Handle an INT signal
122
+ #
123
+ def handle_interrupt
124
+ puts "SIGINT received"
125
+ terminate_gracefully
126
+ end
127
+
128
+ # Handle a HUP signal
129
+ #
130
+ def handle_hangup
131
+ puts "SIGHUP received"
132
+ terminate_gracefully
133
+ end
134
+
56
135
  # Register a process to be run by this +Engine+
57
136
  #
58
137
  # @param [String] name A name for this process
@@ -98,11 +177,11 @@ class Foreman::Engine
98
177
  end
99
178
  end
100
179
 
101
- # Send a signal to all processesstarted by this +Engine+
180
+ # Send a signal to all processes started by this +Engine+
102
181
  #
103
182
  # @param [String] signal The signal to send to each process
104
183
  #
105
- def killall(signal="SIGTERM")
184
+ def kill_children(signal="SIGTERM")
106
185
  if Foreman.windows?
107
186
  @running.each do |pid, (process, index)|
108
187
  system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
@@ -111,6 +190,21 @@ class Foreman::Engine
111
190
  rescue Errno::ESRCH, Errno::EPERM
112
191
  end
113
192
  end
193
+ else
194
+ begin
195
+ Process.kill signal, *@running.keys unless @running.empty?
196
+ rescue Errno::ESRCH, Errno::EPERM
197
+ end
198
+ end
199
+ end
200
+
201
+ # Send a signal to the whole process group.
202
+ #
203
+ # @param [String] signal The signal to send
204
+ #
205
+ def killall(signal="SIGTERM")
206
+ if Foreman.windows?
207
+ kill_children(signal)
114
208
  else
115
209
  begin
116
210
  Process.kill "-#{signal}", Process.getpgrp
@@ -277,8 +371,22 @@ private
277
371
  Thread.new do
278
372
  begin
279
373
  loop do
280
- io = IO.select(@readers.values, nil, nil, 30)
374
+ io = IO.select([@selfpipe[:reader]] + @readers.values, nil, nil, 30)
375
+
376
+ begin
377
+ @selfpipe[:reader].read_nonblock(11)
378
+ rescue Errno::EAGAIN, Errno::EINTR => err
379
+ # ignore
380
+ end
381
+
382
+ # Look for any signals that arrived and handle them
383
+ while sig = Thread.main[:signal_queue].shift
384
+ self.handle_signal(sig)
385
+ end
386
+
281
387
  (io.nil? ? [] : io.first).each do |reader|
388
+ next if reader == @selfpipe[:reader]
389
+
282
390
  if reader.eof?
283
391
  @readers.delete_if { |key, value| value == reader }
284
392
  else
@@ -305,13 +413,14 @@ private
305
413
 
306
414
  def terminate_gracefully
307
415
  return if @terminating
416
+ restore_default_signal_handlers
308
417
  @terminating = true
309
418
  if Foreman.windows?
310
419
  system "sending SIGKILL to all processes"
311
- killall "SIGKILL"
420
+ kill_children "SIGKILL"
312
421
  else
313
422
  system "sending SIGTERM to all processes"
314
- killall "SIGTERM"
423
+ kill_children "SIGTERM"
315
424
  end
316
425
  Timeout.timeout(options[:timeout]) do
317
426
  watch_for_termination while @running.length > 0
@@ -1,5 +1,5 @@
1
1
  module Foreman
2
2
 
3
- VERSION = "0.61.0"
3
+ VERSION = "0.62.0"
4
4
 
5
5
  end
@@ -41,7 +41,7 @@ describe Foreman::Process do
41
41
 
42
42
  it "should output utf8 properly" do
43
43
  process = Foreman::Process.new(resource_path("bin/utf8"))
44
- run(process).should == "\xFF\x03\n"
44
+ run(process).should == "\xFF\x03\n".force_encoding('binary')
45
45
  end
46
46
  end
47
47
 
metadata CHANGED
@@ -1,27 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.61.0
5
- prerelease:
4
+ version: 0.62.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - David Dollar
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-01-14 00:00:00.000000000 Z
11
+ date: 2013-03-08 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: thor
16
- requirement: &70365748678500 !ruby/object:Gem::Requirement
17
- none: false
15
+ requirement: !ruby/object:Gem::Requirement
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: 0.13.6
22
20
  type: :runtime
23
21
  prerelease: false
24
- version_requirements: *70365748678500
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.13.6
25
27
  description: Process manager for applications with multiple components
26
28
  email: ddollar@gmail.com
27
29
  executables:
@@ -114,33 +116,26 @@ files:
114
116
  homepage: http://github.com/ddollar/foreman
115
117
  licenses:
116
118
  - MIT
119
+ metadata: {}
117
120
  post_install_message:
118
121
  rdoc_options: []
119
122
  require_paths:
120
123
  - lib
121
124
  required_ruby_version: !ruby/object:Gem::Requirement
122
- none: false
123
125
  requirements:
124
- - - ! '>='
126
+ - - '>='
125
127
  - !ruby/object:Gem::Version
126
128
  version: '0'
127
- segments:
128
- - 0
129
- hash: -555731677755334112
130
129
  required_rubygems_version: !ruby/object:Gem::Requirement
131
- none: false
132
130
  requirements:
133
- - - ! '>='
131
+ - - '>='
134
132
  - !ruby/object:Gem::Version
135
133
  version: '0'
136
- segments:
137
- - 0
138
- hash: -555731677755334112
139
134
  requirements: []
140
135
  rubyforge_project:
141
- rubygems_version: 1.8.11
136
+ rubygems_version: 2.0.0
142
137
  signing_key:
143
- specification_version: 3
138
+ specification_version: 4
144
139
  summary: Process manager for applications with multiple components
145
140
  test_files: []
146
141
  has_rdoc: