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

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