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