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
|
-
|
4
|
+
ODAwZWNlOGQwZjUzMTI0MjBlODhhZmE4YzlmYjU2MDY3ZDgzNTdkMA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjAxYmQ1YWEzN2ZkNDU0Y2U3ZWZiYmZmZmEwZDE3NTcxZjM2NTg4YQ==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MTk5MjBkNzhiZWI1OTA5ZWVjNjM5ODkzMTE1ODA4YTk0NzA4YmQwNTA5ZWZi
|
10
|
+
ODNjY2ViYTJmZjQ0ODk3OWVhYjEyYjcxMjJjNWEzYTJjOGM3ODMyYzRiMDUx
|
11
|
+
NGUzNmVhNTlkMDc2MjdhMmFkNTk2ZTg2M2JkMDRiYThmNjA3MWI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MjE5MDE0ZGUyM2EzNjVmZGMwMjA4YjNmNmM1NGJjMDM2ZWRkMmI5OGU3ZjNi
|
14
|
+
ODVmZDcxZjIyZGI0ZDIxMDU0MTFhMDFkNjMyN2FkNTE3NTNiZTFkMTM2OTA3
|
15
|
+
NzljZjI0MmQ3MWRkMmZkZTgwM2JhMWY5NGMyY2I1N2MyOTE2YmQ=
|
data/lib/derelict/executer.rb
CHANGED
@@ -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
|
-
|
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,
|
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)
|
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
|
-
|
99
|
-
|
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)
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
-
# *
|
126
|
-
# *
|
127
|
-
# * block:
|
128
|
-
def handle_streams(
|
129
|
-
|
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
|
-
|
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 ==
|
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
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
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 |
|
26
|
-
|
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 }
|