shexecutor 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 610b8828b18a5c04454d349966788f26997f6f12
4
+ data.tar.gz: f9e6d63577e7acd0fb79001863c6f67a4f901e0e
5
+ SHA512:
6
+ metadata.gz: b91c99fd12ce80faaf82636b2bb4f4b055ca803674eb5f6298442d7d7bb5f6462719a42c085a3a273f66198c8d9e5ef14ce5ece0586dda73b96dc7f67c32216b
7
+ data.tar.gz: ce988ff050c5e011268683556073f43c8b2ff55d393052675a3e509d734d03cbea3a141818a18389d1ee070ec4579c48a3fb7ad6bd0077f2d0418580f694d2e7
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *swp
2
+ coverage
3
+ *gem
4
+ doc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in shexecutor.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,47 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ shexecutor (0.0.6)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ byebug (4.0.5)
10
+ columnize (= 0.9.0)
11
+ columnize (0.9.0)
12
+ diff-lcs (1.2.5)
13
+ docile (1.1.5)
14
+ json (1.8.2)
15
+ rake (10.4.2)
16
+ rspec (3.2.0)
17
+ rspec-core (~> 3.2.0)
18
+ rspec-expectations (~> 3.2.0)
19
+ rspec-mocks (~> 3.2.0)
20
+ rspec-core (3.2.3)
21
+ rspec-support (~> 3.2.0)
22
+ rspec-expectations (3.2.1)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.2.0)
25
+ rspec-mocks (3.2.1)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.2.0)
28
+ rspec-support (3.2.2)
29
+ simplecov (0.10.0)
30
+ docile (~> 1.1.0)
31
+ json (~> 1.8)
32
+ simplecov-html (~> 0.10.0)
33
+ simplecov-html (0.10.0)
34
+ simplecov-rcov (0.2.3)
35
+ simplecov (>= 0.4.1)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ bundler
42
+ byebug
43
+ rake
44
+ rspec
45
+ shexecutor!
46
+ simplecov
47
+ simplecov-rcov
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Ernst van Graan
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # SHExecutor
2
+
3
+ SHExecutor is a convenience wrapper for executing shell commands from with-in a ruby process. It is intended to be used for the simple execution of shell commands or scripts that are not expected to generate GBs of output or require feeding via stdin.
4
+
5
+ ## It supports:
6
+ - blocking indefinitely, on exit you can get status, stdout and stderr
7
+ - blocking with timeout, on exit you can get status, stdout, stderr, timeout exception (then output streams are not available)
8
+ - non-blocking, then you get no output streams, but status of the forked process
9
+ - replacement, then you get a replaced process and none of the above
10
+
11
+ ## It is intended to support (future release):
12
+ - redirecting of stderr and stdout, separately, to file, with the option to overwrite or append
13
+
14
+ ## It does not support:
15
+ - streaming input to stdin
16
+ - large stdout and stderr outputs that require real-time reading from the pipes
17
+
18
+ ## For any executor:
19
+ - you can ask status, which will tell you "not executed", current status (e.g. run or sleep) and "no longer executing"
20
+ - you can ask result, which gives you nil unless the process is no longer executing, in which case you get status (e.g. pid, exit code, sigints.)
21
+
22
+ For a full description of status, please see Process::Status
23
+
24
+ This gem is sponsored by Hetzner (Pty) Ltd - http://hetzner.co.za
25
+
26
+ ## Installation
27
+
28
+ Add this line to your application's Gemfile:
29
+
30
+ gem 'shexecutor'
31
+
32
+ And then execute:
33
+
34
+ $ bundle
35
+
36
+ Or install it yourself as:
37
+
38
+ $ gem install shexecutor
39
+
40
+ ## Usage
41
+
42
+ ###Blocking
43
+
44
+ ```
45
+ iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => "/bin/echo", :params => ["hello world"]})
46
+ iut.execute
47
+ iut.flush
48
+ puts "After execution status is: #{iut.status}"
49
+ # "no longer executing"
50
+ puts "out: #{iut.stdout} err: #{iut.stderr}"
51
+ puts "#{iut.result.pid} success? #{iut.result.success?} with code #{iut.result.exitstatus}"
52
+ puts "For more see: #{iut.result.methods}"
53
+ ```
54
+
55
+ ###Blocking with timeout
56
+
57
+ ```
58
+ iut = SHExecutor::Executor.new({:timeout => 1, :wait_for_completion => true, :application_path => "/bin/sleep", :params => ["2"]})
59
+ iut.execute
60
+ # Timeout::Error gets raised and the spawned process is killed
61
+ ```
62
+
63
+ ###Non-blocking
64
+
65
+ ```
66
+ iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => "/bin/sleep", :params => ["1"]})
67
+ iut.execute
68
+ puts "Status: #{iut.status}"
69
+ # "run" or "sleep"
70
+ sleep 2
71
+ puts "Status: #{iut.status}"
72
+ # "no longer executing"
73
+ ```
74
+
75
+ ###Replacing
76
+
77
+ ```
78
+ iut = SHExecutor::Executor.new({:replace => true, :application_path => "/bin/echo", :params => ["Your process has been assimilated"]})
79
+ iut.execute
80
+ ```
81
+
82
+ ###stdout and stderr
83
+
84
+ ```
85
+ iut.flush
86
+ ```
87
+
88
+ Remember to call iut.flush in order to stream stdout and stderr to the Executor object.
89
+
90
+ Note: calling flush will block if execute was called non-blocking, so wait until the execute has finished to preserve non-blocking behaviour.
91
+
92
+ Please send feedback and comments to the author at:
93
+
94
+ Ernst van Graan <ernst.van.graan@hetzner.co.za>
95
+
96
+ ## Contributing
97
+
98
+ 1. Fork it ( https://github.com/[my-github-username]/executor/fork )
99
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
100
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
101
+ 4. Push to the branch (`git push origin my-new-feature`)
102
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,3 @@
1
+ module SHExecutor
2
+ VERSION = "0.0.6"
3
+ end
data/lib/shexecutor.rb ADDED
@@ -0,0 +1,132 @@
1
+ require "shexecutor/version"
2
+ require 'open3'
3
+
4
+ module SHExecutor
5
+ @@default_options = {
6
+ :timeout => -1,
7
+ :protect_against_injection => true,
8
+ :stdout_path => nil,
9
+ :strerr_path => nil,
10
+ :append_stdout_path => true,
11
+ :append_stderr_path => true,
12
+ :replace => false,
13
+ :wait_for_completion => false
14
+ }
15
+
16
+ def self.default_options
17
+ @@default_options
18
+ end
19
+
20
+ class Executor
21
+ attr_accessor :options
22
+ attr_accessor :stdout
23
+ attr_accessor :stderr
24
+ attr_accessor :result
25
+ attr_accessor :pid
26
+
27
+ def initialize(options = ::SHExecutor::default_options)
28
+ # set default options
29
+ @options = ::SHExecutor::default_options.dup
30
+
31
+ # then apply specified options
32
+ options.each do |key, value|
33
+ @options[key] = value
34
+ end
35
+
36
+ @options
37
+ end
38
+
39
+ def status
40
+ return "not executed" if @result.nil?
41
+ return @result.status if @result.alive?
42
+ return "no longer executing" if not @result.alive?
43
+ end
44
+
45
+ def result
46
+ return nil if status != "no longer executing"
47
+ @result.value
48
+ end
49
+
50
+ def validate
51
+ errors = []
52
+ if (!@options[:application_path].nil? and @options[:application_path].strip != "")
53
+ if (File.exists?(@options[:application_path]))
54
+ errors << "Application path not executable" if !File.executable?(@options[:application_path])
55
+ else
56
+ errors << "Application path not found"
57
+ end
58
+
59
+ errors << "Suspected injection vulnerability due to space in application_path. Turn off strict checking if you are sure" if @options[:application_path].include?(" ")
60
+
61
+ else
62
+ errors << "No application path provided" if (@options[:application_path].nil?) or (@options[:application_path].strip == "")
63
+ end
64
+
65
+ raise ArgumentError.new(errors.join(',')) if errors.count > 0
66
+ end
67
+
68
+ def execute
69
+ @pid = nil
70
+ @stdout = nil
71
+ @stderr = nil
72
+ if (@options[:replace] == true)
73
+ replace_process
74
+ else
75
+ if (@options[:wait_for_completion])
76
+ if (@options[:timeout] <= 0)
77
+ block_process
78
+ else
79
+ block_process_with_timeout
80
+ end
81
+ else
82
+ fork_process
83
+ end
84
+ end
85
+ end
86
+
87
+ # Warning! This method will block if you are not blocking!
88
+ # Check status? before calling this to see if the process has completed if you do not want to block
89
+ def flush
90
+ #store output for inspection
91
+ @stdout = @stdout_stream.gets(nil)
92
+ @stderr = @stderr_stream.gets(nil)
93
+ end
94
+
95
+ private
96
+
97
+ def replace_process
98
+ validate
99
+ @options[:params].nil? ? exec(@options[:application_path]) : exec(@options[:application_path], *@options[:params])
100
+ end
101
+
102
+ def block_process
103
+ validate
104
+ @stdin_stream, @stdout_stream, @stderr_stream, @result = Open3::popen3(@options[:application_path], *@options[:params])
105
+ @result.value
106
+ end
107
+
108
+ def block_process_with_timeout
109
+ validate
110
+ @stdin_stream, @stdout_stream, @stderr_stream, @result = Open3::popen3(@options[:application_path], *@options[:params])
111
+ begin
112
+ Timeout.timeout(@options[:timeout]) do
113
+ @result.value
114
+ end
115
+ rescue Timeout::Error => ex
116
+ Process.kill 9, @result.pid
117
+ raise ex
118
+ end
119
+ end
120
+
121
+ def fork_process
122
+ validate
123
+ @stdin_stream, @stdout_stream, @stderr_stream, @result = Open3::popen3(@options[:application_path], *@options[:params])
124
+ end
125
+ end
126
+ end
127
+
128
+ #def redirect_all_output_to_file file
129
+ # log = File.new(file, "w+")
130
+ # STDOUT.reopen log; STDERR.reopen log
131
+ # STDOUT.sync = true
132
+ #end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'shexecutor/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "shexecutor"
8
+ spec.version = SHExecutor::VERSION
9
+ spec.authors = ["Ernst van Graan"]
10
+ spec.email = ["ernst.van.graan@hetzner.co.za"]
11
+ spec.summary = %q{Execute shell commands easily and securely}
12
+ spec.description = %q{Implements process replacement, forking, protection from shell injection, and a variety of output options}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency 'bundler'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'simplecov'
25
+ spec.add_development_dependency 'simplecov-rcov'
26
+ spec.add_development_dependency 'byebug'
27
+ end
@@ -0,0 +1,306 @@
1
+ require 'spec_helper'
2
+ require 'shexecutor.rb'
3
+ require 'tempfile'
4
+ require 'mocks/kernel.rb'
5
+ require 'mocks/result.rb'
6
+
7
+ describe 'Executor' do
8
+ before :all do
9
+ @executable_file = Tempfile.new("testingargumenterror")
10
+ File.chmod(0744, @executable_file.path)
11
+ `echo "ls" >> #{@executable_file.path}`
12
+ end
13
+
14
+ context 'when initialized with no options' do
15
+ it 'should set default options' do
16
+ @iut = SHExecutor::Executor.new
17
+ end
18
+ end
19
+
20
+ context 'when initialized with options' do
21
+ it 'should remember the options' do
22
+ iut_options = {
23
+ :timeout => -1,
24
+ :protect_against_injection=>true,
25
+ :stdout_path => '/log/testlog',
26
+ :strerr_path => '/log/testlog',
27
+ :append_stdout_path => false,
28
+ :append_stderr_path => false,
29
+ :replace => false,
30
+ :wait_for_completion => true
31
+ }
32
+ @iut = SHExecutor::Executor.new(iut_options)
33
+ expect(@iut.options).to eq(iut_options)
34
+ end
35
+ end
36
+
37
+ context 'when initialized with some but not all options' do
38
+ it 'should remember the options provided and default the rest' do
39
+ expected_options = ::SHExecutor::default_options
40
+ expected_options[:wait_for_completion] = false
41
+ iut_options = {
42
+ :wait_for_completion => false
43
+ }
44
+ @iut = SHExecutor::Executor.new(iut_options)
45
+ expect(@iut.options).to eq(expected_options)
46
+ end
47
+ end
48
+
49
+ context 'when initialized with one or more invalid options' do
50
+ it 'validate should raise an ArgumentError' do
51
+ iut = SHExecutor::Executor.new({:application_path => nil})
52
+ expect{
53
+ iut.validate
54
+ }.to raise_error(ArgumentError, "No application path provided")
55
+ iut = SHExecutor::Executor.new({:application_path => ""})
56
+ expect{
57
+ iut.validate
58
+ }.to raise_error(ArgumentError, "No application path provided")
59
+ iut = SHExecutor::Executor.new({:application_path => "/kjsdfhgjkgsjk"})
60
+ expect{
61
+ iut.validate
62
+ }.to raise_error(ArgumentError, "Application path not found")
63
+ f = Tempfile.new("testingargumenterror")
64
+ iut = SHExecutor::Executor.new({:application_path => f.path})
65
+ expect{
66
+ iut.validate
67
+ }.to raise_error(ArgumentError, "Application path not executable")
68
+ end
69
+ end
70
+
71
+ context 'when initialized with valid options' do
72
+ it 'validate should not raise an exception' do
73
+ iut = SHExecutor::Executor.new({:application_path => @executable_file.path})
74
+ iut.validate
75
+ end
76
+ end
77
+
78
+ context "when asked to execute" do
79
+ it 'should clear stderr and stdout before execution' do
80
+ test_command = "/bin/echo"
81
+ test_params = ["Hello"]
82
+ iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => test_command, :params => test_params})
83
+ iut.stdout = "stdout before"
84
+ iut.stderr = "error before"
85
+ iut.execute
86
+ iut.flush
87
+ expect(iut.stderr).to be_nil
88
+ expect(iut.stdout).to eq("Hello\n")
89
+ end
90
+ end
91
+
92
+ context 'when asked to execute, replacing the current process' do
93
+ it 'should use exec with the command and parameters specified' do
94
+ test_command = "/bin/ls"
95
+ test_params = ["/tmp/"]
96
+ iut = SHExecutor::Executor.new({:replace => true, :application_path => test_command, :params => test_params})
97
+ iut.execute
98
+ expect(Kernel::last_command).to eq(test_command)
99
+ expect(Kernel::last_params).to eq(*test_params)
100
+ end
101
+
102
+ it 'should use exec with the command if no parameters are specified' do
103
+ test_command = "/bin/ls"
104
+ iut = SHExecutor::Executor.new({:replace => true, :application_path => test_command})
105
+ iut.execute
106
+ expect(Kernel::last_command).to eq(test_command)
107
+ expect(Kernel::last_params).to be_nil
108
+ end
109
+
110
+ it 'should validate' do
111
+ iut = SHExecutor::Executor.new({:replace => true})
112
+ expect{
113
+ iut.execute
114
+ }.to raise_error(ArgumentError, "No application path provided")
115
+ end
116
+
117
+ it 'should not replace if replace is not true' do
118
+ iut = SHExecutor::Executor.new({:application_path => "/bin/ls", :replace => false})
119
+ Kernel::last_command = "not executed"
120
+ iut.execute
121
+ expect(Kernel::last_command).to eq("not executed")
122
+
123
+ iut = SHExecutor::Executor.new({:application_path => "/bin/ls", :replace => 'blah ignore this'})
124
+ iut.execute
125
+ expect(Kernel::last_command).to eq("not executed")
126
+ end
127
+ end
128
+
129
+ context 'when not waiting for completion' do
130
+ it 'should execute the command without blocking' do
131
+ test_command = "/bin/sleep"
132
+ test_params = ["2"]
133
+ iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => test_command, :params => test_params})
134
+ before = Time.now
135
+ iut.execute
136
+ after = Time.now
137
+ expect(after - before).to be < 0.2
138
+ end
139
+
140
+ it 'should validate' do
141
+ iut = SHExecutor::Executor.new({:wait_for_completion => false})
142
+ expect{
143
+ iut.execute
144
+ }.to raise_error(ArgumentError, "No application path provided")
145
+ end
146
+ end
147
+
148
+ context 'when asking for the status of the executor' do
149
+ it 'should return "not executed" if execute has not been called' do
150
+ test_command = "/bin/sleep"
151
+ test_params = ["2"]
152
+ iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => test_command, :params => test_params})
153
+ expect(iut.status).to eq("not executed")
154
+ end
155
+
156
+ it 'should return the current process status if the process is executing' do
157
+ test_command = "/bin/sleep"
158
+ test_params = ["2"]
159
+ iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => test_command, :params => test_params})
160
+ iut.execute
161
+ status = iut.status
162
+ puts "status: #{status}"
163
+ running = ((status == "run") or (status == "sleep"))
164
+ expect(running).to eq(true)
165
+ end
166
+
167
+ it 'should return "no longer executing" if the process has stopped for what-ever reason' do
168
+ test_command = "/bin/echo"
169
+ test_params = ["I ran"]
170
+ iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => test_command, :params => test_params})
171
+ iut.execute
172
+ sleep 1
173
+ expect(iut.status).to eq("no longer executing")
174
+ end
175
+ end
176
+
177
+ context 'when asking for the result of execution' do
178
+ it 'should return nil if the process has not executed yet' do
179
+ test_command = "/bin/sleep"
180
+ test_params = ["1"]
181
+ iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => test_command, :params => test_params})
182
+ expect(iut.result).to be_nil
183
+ end
184
+
185
+ it 'should return nil if the process is still executing' do
186
+ test_command = "/bin/sleep"
187
+ test_params = ["1"]
188
+ iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => test_command, :params => test_params})
189
+ iut.execute
190
+ expect(iut.result).to be_nil
191
+ end
192
+
193
+ it 'should return a Process::Status object if the process has executed, but is no longer, for what-ever reason' do
194
+ test_command = "/bin/sleep"
195
+ test_params = ["1"]
196
+ iut = SHExecutor::Executor.new({:wait_for_completion => false, :application_path => test_command, :params => test_params})
197
+ iut.execute
198
+ sleep 2
199
+ expect(iut.result.class).to eq(Process::Status)
200
+ end
201
+ end
202
+
203
+ context 'when asked to execute and block' do
204
+ it 'should raise a TimeoutException if a timeout is specified and the forked process does not exit before' do
205
+ test_command = "/bin/sleep"
206
+ test_params = ["2"]
207
+ iut = SHExecutor::Executor.new({:timeout => 1, :wait_for_completion => true, :application_path => test_command, :params => test_params})
208
+ before = Time.now
209
+ expect {
210
+ iut.execute
211
+ }.to raise_error(Timeout::Error, "execution expired")
212
+ after = Time.now
213
+ expect(after - before).to be < 2
214
+ end
215
+
216
+ it 'should kill the subprocess when a TimeoutException is raised' do
217
+ test_command = "/bin/sleep"
218
+ test_params = ["2"]
219
+ iut = SHExecutor::Executor.new({:timeout => 1, :wait_for_completion => true, :application_path => test_command, :params => test_params})
220
+ expect(Process).to receive(:kill)
221
+ expect {
222
+ iut.execute
223
+ }.to raise_error(Timeout::Error, "execution expired")
224
+ end
225
+
226
+ it 'should use Open3::popen3 with the command and parameters specified' do
227
+ test_command = "/bin/ls"
228
+ test_params = ["/tmp/"]
229
+ iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => test_command, :params => test_params})
230
+ stdin = stdout = stderr = StringIO.new
231
+ expect(Open3).to receive(:popen3).with(test_command, *test_params).and_return([stdin, stdout, stderr, Result.new(true)])
232
+ iut.execute
233
+ end
234
+
235
+ it 'should use Open3::popen3 with the command' do
236
+ test_command = "/bin/ls"
237
+ iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => test_command})
238
+ stdin = stdout = stderr = StringIO.new
239
+ expect(Open3).to receive(:popen3).with(test_command).and_return([stdin, stdout, stderr, Result.new(true)])
240
+ iut.execute
241
+ end
242
+
243
+ it 'should block until completion' do
244
+ test_command = "/bin/sleep"
245
+ test_params = ["2"]
246
+ iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => test_command, :params => test_params})
247
+ before = Time.now
248
+ iut.execute
249
+ after = Time.now
250
+ expect(after - before).to be > 2
251
+ end
252
+
253
+ it 'should validate' do
254
+ iut = SHExecutor::Executor.new({:wait_for_completion => true})
255
+ expect{
256
+ iut.execute
257
+ }.to raise_error(ArgumentError, "No application path provided")
258
+ end
259
+ end
260
+
261
+ context 'when asked to redirect stdout to a file appending' do
262
+ it 'should append stdout to the file specified, and create it if it does not exist'
263
+ end
264
+
265
+ context 'when asked to redirect stdout to a file overwriting' do
266
+ it 'should delete if exists and create the specified file, then append stdout to it'
267
+ end
268
+
269
+ context 'when asked to redirect stdout' do
270
+ it 'should raise an exception if one of the file operations fails'
271
+ end
272
+
273
+ context 'when asked to redirect stderr to a file appending' do
274
+ it 'should append stderr to the file specified, and create it if it does not exist'
275
+ end
276
+
277
+ context 'when asked to redirect stderr to a file overwriting' do
278
+ it 'should delete if exists and create the specified file, then append stderr to it'
279
+ end
280
+
281
+ context 'when asked to redirect stderr' do
282
+ it 'should raise an exception if one of the file operations fails'
283
+ end
284
+
285
+ context 'when asked to flush' do
286
+ it 'should flush to its buffer for stderr' do
287
+ test_command = "/bin/echo"
288
+ test_params = ["hello world"]
289
+ iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => test_command, :params => test_params})
290
+ iut.execute
291
+ iut.flush
292
+ expect(iut.stdout).to eq("hello world\n")
293
+ expect(iut.stderr).to be_nil
294
+ end
295
+
296
+ it 'should flush to its buffer for stdout' do
297
+ test_command = "/bin/ls"
298
+ test_params = ["/tmp/thisfiledoesnotexistsatgup80wh0hgoefhgohuo4whg4whg4w5hg0"]
299
+ iut = SHExecutor::Executor.new({:wait_for_completion => true, :application_path => test_command, :params => test_params})
300
+ iut.execute
301
+ iut.flush
302
+ expect(iut.stdout).to be_nil
303
+ expect(iut.stderr).to eq("ls: /tmp/thisfiledoesnotexistsatgup80wh0hgoefhgohuo4whg4whg4w5hg0: No such file or directory\n")
304
+ end
305
+ end
306
+ end
@@ -0,0 +1,22 @@
1
+ module Kernel
2
+ @@last_command = nil
3
+ @@last_params = nil
4
+
5
+ def exec(cmd, params = nil)
6
+ @@last_command = cmd
7
+ @@last_params = params
8
+ puts "Kernel::exec('#{cmd}', '#{params}')"
9
+ end
10
+
11
+ def last_command=(command)
12
+ @@last_command = command
13
+ end
14
+
15
+ def last_command
16
+ @@last_command
17
+ end
18
+
19
+ def last_params
20
+ @@last_params
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ class Result
2
+ attr_accessor :value
3
+
4
+ def initialize(value)
5
+ @value = value
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ require 'rspec'
2
+ require 'rspec/mocks'
3
+ require 'tempfile'
4
+ require 'simplecov'
5
+ require 'simplecov-rcov'
6
+ require 'byebug'
7
+
8
+ # This file was generated by the `rspec --init` command. Conventionally, all
9
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
10
+ # Require this file using `require "spec_helper"` to ensure that it is only
11
+ # loaded once.
12
+ #
13
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'executor'))
16
+
17
+ RSpec.configure do |config|
18
+ config.run_all_when_everything_filtered = true
19
+ config.filter_run :focus
20
+
21
+ # Run specs in random order to surface order dependencies. If you find an
22
+ # order dependency and want to debug it, you can fix the order by providing
23
+ # the seed, which is printed after each run.
24
+ # --seed 1234
25
+ config.order = 'random'
26
+ end
27
+
28
+ SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
29
+ SimpleCov.start do
30
+ add_filter "/spec/"
31
+ end
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shexecutor
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.6
5
+ platform: ruby
6
+ authors:
7
+ - Ernst van Graan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: simplecov
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov-rcov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Implements process replacement, forking, protection from shell injection,
98
+ and a variety of output options
99
+ email:
100
+ - ernst.van.graan@hetzner.co.za
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - .gitignore
106
+ - Gemfile
107
+ - Gemfile.lock
108
+ - LICENSE.txt
109
+ - README.md
110
+ - Rakefile
111
+ - lib/shexecutor.rb
112
+ - lib/shexecutor/version.rb
113
+ - shexecutor.gemspec
114
+ - spec/executor_spec.rb
115
+ - spec/mocks/kernel.rb
116
+ - spec/mocks/result.rb
117
+ - spec/spec_helper.rb
118
+ homepage: ''
119
+ licenses:
120
+ - MIT
121
+ metadata: {}
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 2.0.14
139
+ signing_key:
140
+ specification_version: 4
141
+ summary: Execute shell commands easily and securely
142
+ test_files:
143
+ - spec/executor_spec.rb
144
+ - spec/mocks/kernel.rb
145
+ - spec/mocks/result.rb
146
+ - spec/spec_helper.rb