derelict 0.4.2.travis.118 → 0.4.2.travis.120

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NGRhZDE3YWQyNDRhZmZlYzQwMDYyZWVhN2M3MTU2MjM0ZTJjOTY2MA==
4
+ ODAwZWNlOGQwZjUzMTI0MjBlODhhZmE4YzlmYjU2MDY3ZDgzNTdkMA==
5
5
  data.tar.gz: !binary |-
6
- NjliY2FlNzI2OTQxOWI3OGQyMWMwMThlMmYzNzJjZjBhYWM0ZGE1Ng==
6
+ YjAxYmQ1YWEzN2ZkNDU0Y2U3ZWZiYmZmZmEwZDE3NTcxZjM2NTg4YQ==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OGE3OWJmZDQ2NmYwMWEwYWQwMTI5ZDk4NjRmMWFhZGNhMjdmMjE0MmFkOGM2
10
- NTJmMDA0YTBkZWI4ZjdjNzY4N2JmZDkxMTA0ZjM0NTk2NDFlMTg3NmI5ZWM2
11
- MjgxMzU2NDdiZWMxNTUyZTUwZDdjM2QzZmEyNDI5ZmJjNzM3YjY=
9
+ MTk5MjBkNzhiZWI1OTA5ZWVjNjM5ODkzMTE1ODA4YTk0NzA4YmQwNTA5ZWZi
10
+ ODNjY2ViYTJmZjQ0ODk3OWVhYjEyYjcxMjJjNWEzYTJjOGM3ODMyYzRiMDUx
11
+ NGUzNmVhNTlkMDc2MjdhMmFkNTk2ZTg2M2JkMDRiYThmNjA3MWI=
12
12
  data.tar.gz: !binary |-
13
- NzZiYjNlOTQxNDFjNTQ3OWQ3YWFjNjhjMDA0MjE0NGZmNGM5NjQxYjMwYmJl
14
- Zjc3OTU1YmM1Y2MzNDEzMmU3YTRiN2E4ZTMwYmIxNjQ3NDI2YjgwMjc0NzJl
15
- N2RhN2UzNTQxOTE5ZGY1YzBkOGZhMThmNWEzY2ZmMGFmNTk2MDk=
13
+ MjE5MDE0ZGUyM2EzNjVmZGMwMjA4YjNmNmM1NGJjMDM2ZWRkMmI5OGU3ZjNi
14
+ ODVmZDcxZjIyZGI0ZDIxMDU0MTFhMDFkNjMyN2FkNTE3NTNiZTFkMTM2OTA3
15
+ NzljZjI0MmQ3MWRkMmZkZTgwM2JhMWY5NGMyY2I1N2MyOTE2YmQ=
@@ -4,7 +4,10 @@ module Derelict
4
4
  # The safety involved is mainly ensuring that the command is
5
5
  # gracefully terminated if this process is about to terminate.
6
6
  class Executer
7
- attr_reader :stdout, :stderr
7
+ # Include "logger" method to get a logger for this class
8
+ include Utils::Logger
9
+
10
+ attr_reader :stdout, :stderr, :pid
8
11
 
9
12
  # Executes <tt>command</tt> and returns after execution
10
13
  #
@@ -43,6 +46,8 @@ module Derelict
43
46
  def initialize(options = {})
44
47
  @options = {:mode => :lines, :no_buffer => false}.merge(options)
45
48
 
49
+ logger.info "Initializing with options: #{@options.inspect}"
50
+
46
51
  if @options[:mode] == :chars
47
52
  @reader = proc {|s| s.getc }
48
53
  else
@@ -61,12 +66,22 @@ module Derelict
61
66
  # second parameter is stderr; only one will contain
62
67
  # data, the other will be nil)
63
68
  def execute(command, &block)
69
+ logger.info "Executing command '#{command}'"
64
70
  reset
65
- pid, stdin, stdout, stderr = Open4::popen4(command)
71
+ pid, stdin, stdout_stream, stderr_stream = Open4::popen4(command)
72
+ @pid = pid
73
+ stdin.close rescue nil
66
74
 
67
75
  save_exit_status(pid)
68
- forward_signals_to(pid) { handle_streams stdout, stderr, &block }
76
+ forward_signals_to(pid) do
77
+ handle_streams stdout_stream, stderr_stream, &block
78
+ end
79
+
69
80
  self
81
+ ensure
82
+ logger.debug "Closing stdout and stderr streams for process"
83
+ stdout.close rescue nil
84
+ stderr.close rescue nil
70
85
  end
71
86
 
72
87
  # Determines whether the last command was successful or not
@@ -84,9 +99,11 @@ module Derelict
84
99
  # This is done when first initialising, and just before a command
85
100
  # is run, to get rid of the previous command's data.
86
101
  def reset
102
+ logger.debug "Resetting executer state"
87
103
  @stdout = ''
88
104
  @stderr = ''
89
105
  @success = nil
106
+ @pid = nil
90
107
  end
91
108
 
92
109
  # Waits for the exit status of a process (in a thread) saving it
@@ -94,9 +111,13 @@ module Derelict
94
111
  # This will set the @status instance variable to true if the exit
95
112
  # status was 0, or false if the exit status was anything else.
96
113
  def save_exit_status(pid)
114
+ logger.debug "Spawning thread to monitor process ID #{pid}"
115
+ @success = nil
97
116
  Thread.start do
98
- @success = nil
99
- @success = (Process.waitpid2(pid).last.exitstatus == 0)
117
+ logger.debug "Thread started, waiting for PID #{pid}"
118
+ status = Process.waitpid2(pid).last.exitstatus
119
+ logger.debug "Process exited with status #{status}"
120
+ @success = (status == 0)
100
121
  end
101
122
  end
102
123
 
@@ -107,26 +128,41 @@ module Derelict
107
128
  # defaults to SIGINT only)
108
129
  def forward_signals_to(pid, signals = %w[INT])
109
130
  # Set up signal handlers
131
+ logger.debug "Setting up signal handlers for #{signals.inspect}"
132
+ signal_count = 0
110
133
  signals.each do |signal|
111
- Signal.trap(signal) { Process.kill signal, pid }
134
+ Signal.trap(signal) do
135
+ Process.kill signal, pid rescue nil
136
+
137
+ # If this is the second time we've received and forwarded
138
+ # on the signal, make sure next time we just give up.
139
+ reset_handlers_for signals if ++signal_count >= 2
140
+ end
112
141
  end
113
142
 
114
143
  # Run the block now that the signals are being forwarded
115
144
  yield
145
+ ensure
146
+ # Always good to make sure we clean up after ourselves
147
+ reset_handlers_for signals
148
+ end
116
149
 
117
- # Reset signal handlers
118
- signals.each do |signal|
119
- Signal.trap signal, "DEFAULT"
120
- end
150
+ # Resets the handlers for particular signals to the default
151
+ #
152
+ # * signals: An array of signal names to reset the handlers for
153
+ def reset_handlers_for(signals)
154
+ logger.debug "Resetting signal handlers for #{signals.inspect}"
155
+ signals.each {|signal| Signal.trap signal, "DEFAULT" }
121
156
  end
122
157
 
123
158
  # Manages reading from the stdout and stderr streams
124
159
  #
125
- # * stdout: The process' stdout stream
126
- # * stderr: The process' stderr stream
127
- # * block: The block to pass any read data to (optional)
128
- def handle_streams(stdout, stderr, &block)
129
- streams = [stdout, stderr]
160
+ # * stdout_stream: The process' stdout stream
161
+ # * stderr_stream: The process' stderr stream
162
+ # * block: The block to pass output to (optional)
163
+ def handle_streams(stdout_stream, stderr_stream, &block)
164
+ logger.debug "Monitoring stdout/stderr streams for output"
165
+ streams = [stdout_stream, stderr_stream]
130
166
  until streams.empty?
131
167
  # Find which streams are ready for reading, timeout 0.1s
132
168
  selected, = select(streams, nil, nil, 0.1)
@@ -136,14 +172,21 @@ module Derelict
136
172
 
137
173
  selected.each do |stream|
138
174
  if stream.eof?
139
- streams.delete(stream) unless @success.nil?
175
+ logger.debug "Stream reached end-of-file"
176
+ if @success.nil?
177
+ logger.debug "Process hasn't finished, keeping stream"
178
+ else
179
+ logger.debug "Removing stream"
180
+ streams.delete(stream)
181
+ end
140
182
  next
141
183
  end
142
184
 
143
185
  while data = @reader.call(stream)
144
186
  data = ((@options[:mode] == :chars) ? data.chr : data)
145
- stream_name = (stream == stdout) ? :stdout : :stderr
187
+ stream_name = (stream == stdout_stream) ? :stdout : :stderr
146
188
  output data, stream_name, &block unless block.nil?
189
+ buffer data, stream_name unless @options[:no_buffer]
147
190
  end
148
191
  end
149
192
  end
@@ -167,14 +210,18 @@ module Derelict
167
210
  else
168
211
  yield data if stream_name == :stdout
169
212
  end
213
+ end
170
214
 
171
- # Add to the buffers
172
- unless @options[:no_buffer]
173
- if stream_name == :stdout
174
- @stdout += data
175
- else
176
- @stderr += data
177
- end
215
+ # Buffers data for a stream into this object to retrieve it later
216
+ #
217
+ # * data: The data that should be added to the buffer
218
+ # * stream_name: Which stream the data came from (:stdout or
219
+ # :stderr)
220
+ def buffer(data, stream_name)
221
+ if stream_name == :stdout
222
+ @stdout += data
223
+ else
224
+ @stderr += data
178
225
  end
179
226
  end
180
227
  end
@@ -22,8 +22,9 @@ module Derelict
22
22
  private
23
23
  # A block that can be passed to #execute to log the output
24
24
  def shell_log_block
25
- Proc.new do |line|
26
- logger(:type => :external).info line
25
+ Proc.new do |stdout, stderr|
26
+ # Only stdout or stderr is populated, the other will be nil
27
+ logger(:type => :external).info(stdout || stderr)
27
28
  end
28
29
  end
29
30
 
@@ -160,15 +160,6 @@ module Derelict
160
160
  connection.execute! *arguments, &block
161
161
  end
162
162
 
163
- # A block that can be passed to #execute to log the output
164
- def shell_log_block
165
- Proc.new do |stdout, stderr|
166
- # Only stdout or stderr is populated, the other will be nil
167
- logger(:type => :external).info(stdout || stderr)
168
- end
169
- end
170
- memoize :shell_log_block
171
-
172
163
  # Retrieves the arguments for a particular action
173
164
  #
174
165
  # * action: The symbol representing the action (one of :up,
@@ -71,7 +71,7 @@ describe Derelict::Box::Manager do
71
71
  subject { manager.add box_name, source, :log => log }
72
72
 
73
73
  before do
74
- expect(instance).to receive(:execute!).with(:box, "add", box_name, source).and_yield("test").and_return(result)
74
+ expect(instance).to receive(:execute!).with(:box, "add", box_name, source).and_yield("test", nil).and_return(result)
75
75
  end
76
76
 
77
77
  it { should be result }
@@ -103,7 +103,7 @@ describe Derelict::Box::Manager do
103
103
  subject { manager.remove box_name, :provider => provider, :log => log }
104
104
 
105
105
  before do
106
- expect(instance).to receive(:execute!).with(:box, "remove", box_name, provider).and_yield("test").and_return(result)
106
+ expect(instance).to receive(:execute!).with(:box, "remove", box_name, provider).and_yield("test", nil).and_return(result)
107
107
  end
108
108
 
109
109
  it { should be result }
@@ -55,6 +55,30 @@ describe Derelict::Executer do
55
55
  let(:command) { "false" }
56
56
  its(:success?) { should be_false }
57
57
  end
58
+
59
+ # Unfortunately this part is even worse. It seems to work though!
60
+ # The basic idea is for a thread to kill *this* process once the
61
+ # sub-process has started. It's still relatively fast, and is at
62
+ # least an accurate way to model the real-world use.
63
+ context "when main process is receives a signal" do
64
+ subject {
65
+ Thread.new do
66
+ # Wait for the sub-process to start
67
+ sleep 0.01 while executer.pid.nil?
68
+
69
+ # Send SIGINT to this process, it should get forwarded on
70
+ # to the sub-process
71
+ Process.kill "INT", Process.pid
72
+ end
73
+
74
+ # Start the sub-process
75
+ executer.execute "sleep 10"
76
+ }
77
+
78
+ specify "the sub-process should get killed" do
79
+ expect(subject.success?).to be_false
80
+ end
81
+ end
58
82
  end
59
83
 
60
84
  context "with a block" do
@@ -91,7 +91,7 @@ describe Derelict::Plugin::Manager do
91
91
  subject { manager.install plugin_name, :version => version, :log => log }
92
92
 
93
93
  before do
94
- expect(instance).to receive(:execute!).with(:plugin, "install", plugin_name, '--plugin-version', version).and_yield("test").and_return(result)
94
+ expect(instance).to receive(:execute!).with(:plugin, "install", plugin_name, '--plugin-version', version).and_yield("test", nil).and_return(result)
95
95
  end
96
96
 
97
97
  it { should be result }
@@ -122,7 +122,7 @@ describe Derelict::Plugin::Manager do
122
122
  subject { manager.uninstall plugin_name, :log => log }
123
123
 
124
124
  before do
125
- expect(instance).to receive(:execute!).with(:plugin, "uninstall", plugin_name).and_yield("test").and_return(result)
125
+ expect(instance).to receive(:execute!).with(:plugin, "uninstall", plugin_name).and_yield("test", nil).and_return(result)
126
126
  end
127
127
 
128
128
  it { should be result }
@@ -153,7 +153,7 @@ describe Derelict::Plugin::Manager do
153
153
  subject { manager.update plugin_name, :log => log }
154
154
 
155
155
  before do
156
- expect(instance).to receive(:execute!).with(:plugin, "update", plugin_name).and_yield("test").and_return(result)
156
+ expect(instance).to receive(:execute!).with(:plugin, "update", plugin_name).and_yield("test", nil).and_return(result)
157
157
  end
158
158
 
159
159
  it { should be result }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: derelict
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2.travis.118
4
+ version: 0.4.2.travis.120
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brad Feehan