remote-exec 0.0.1 → 0.5.0

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,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8abbf419411c6fb963244ec42deb4c7d9703d1c4
4
- data.tar.gz: 684effcebaabb98fa386ca30691c65ac455e5978
3
+ metadata.gz: ca220332149439af73e75f9c632cf6387ae16970
4
+ data.tar.gz: 7f5b2ac70fb84011a475b9bfee2d4e079103ec2b
5
5
  SHA512:
6
- metadata.gz: c22eefd7ccc0b51b8f9cdc336ad2f7bd08729678731b24ea1e5b7e176524d9ab0a31ccba44b0aa8602f23b39d3c9bac4c2de41b0a11bdd72313acd8d24180f7a
7
- data.tar.gz: b5b45613930e25e460b8c03f2adc66315e614fd9a920878d08d04515862f55584767eeeb200ba6bdb2156652eb849a68e77da04834d60d30bd3c4da676d5a796
6
+ metadata.gz: aa812243644b3eebfda7b71741ba1f9556090835cc7759b1a84e09edbbbfbbd9f5f904e2a07e792f598cc876b94170bddebe8f4c561d51cfb68204454bb55e82
7
+ data.tar.gz: f731278f988714a0232ee75ca89653caae39519f0f04ea25a1f9b1ddd84e7fad2898aa227a7d676e04954d4bf5cbba3bf08230da4d7956aaa94a935a85528b50
@@ -0,0 +1,4 @@
1
+ require "remote_exec/version"
2
+ require "remote_exec/fake"
3
+ require "remote_exec/local"
4
+ require "remote_exec/ssh"
@@ -0,0 +1,70 @@
1
+ =begin
2
+ Copyright 2014 Michal Papis <mpapis@gmail.com>
3
+
4
+ See the file LICENSE for copying permission.
5
+ =end
6
+
7
+ require 'ruby/hooks'
8
+ require "remote_exec/version"
9
+
10
+ # Define minimal interface for execution handlers
11
+ class RemoteExec::Base
12
+ extend Ruby::Hooks::InstanceHooks
13
+
14
+ # called before connection attempt will be made, used when connection may fail
15
+ # @param object [Object] the target that invoked the method
16
+ define_hook :before_connect
17
+
18
+ # called when connection attempt failed and we are about to sleep and retry
19
+ # @param object [Object] the target that invoked the method
20
+ # @param exception [Exception] exception that made the connection attempt fail
21
+ # @param retries [Integer] number of retries left
22
+ define_hook :on_connect_retry
23
+
24
+ # called when connection attempt failed and we no more retries left
25
+ # @param object [Object] the target that invoked the method
26
+ # @param exception [Exception] exception that made the connection attempt fail
27
+ define_hook :on_connect_fail
28
+
29
+ # called after connection / session is esatablished
30
+ # @param object [Object] the target that invoked the method
31
+ define_hook :after_connect
32
+
33
+ # called before terminating connection - only when needed
34
+ # @param object [Object] the target that invoked the method
35
+ define_hook :before_shutdown
36
+
37
+ # called before executing command
38
+ # @param object [Object] the target that invoked the method
39
+ # @param command [String] the command to execute
40
+ define_hook :before_execute
41
+
42
+ # called before executing command
43
+ # @param object [Object] the target that invoked the method
44
+ # @param stdout [String] standard output of the command, can be nil
45
+ # @param stderr [String] standard error output of the command, can be nil
46
+ define_hook :on_execute_data
47
+
48
+ # called after executing command
49
+ # @param object [Object] the target that invoked the method
50
+ # @param command [String] the executed command
51
+ # @param result [Integer] the executed command status code (0 - ok, >0 - fail)
52
+ define_hook :after_execute
53
+
54
+ # standard in place handler that ensures shutdown is called
55
+ def initialize
56
+ if block_given?
57
+ begin
58
+ yield self
59
+ ensure
60
+ shutdown
61
+ end
62
+ end
63
+ end
64
+
65
+ # minimal handler for shutdown
66
+ def shutdown
67
+ before_shutdown.changed_and_notify(self)
68
+ end
69
+
70
+ end
@@ -0,0 +1,45 @@
1
+ require "remote_exec/base"
2
+
3
+ # Class to fake running commands and transfering files.
4
+ class RemoteExec::Fake < RemoteExec::Base
5
+
6
+ ##
7
+ # The story to tell in +execute+, take an array
8
+ #
9
+ # @example usage
10
+ #
11
+ # [1, [[nil,"error\n"]]
12
+ #
13
+ # consist of an array: [ return_status, [[ stdout, stderr],...] ]
14
+
15
+ attr_accessor :story
16
+
17
+ # Constructs a new Fake object.
18
+ #
19
+ # @yield [self] if a block is given then the constructed
20
+ # object yields itself and calls `#shutdown` at the end, closing the
21
+ # remote connection
22
+
23
+ def initialize
24
+ after_connect.changed_and_notify(self)
25
+ super
26
+ end
27
+
28
+ ##
29
+ # Execute fake command
30
+ #
31
+ # @param command [String] command string to execute
32
+ # @return [Integer] exit status of the command
33
+
34
+ def execute(command)
35
+ before_execute.changed_and_notify(self, command)
36
+ last_status, outputs = @story
37
+ outputs.each do |out, err|
38
+ on_execute_data.changed_and_notify(self, out, err)
39
+ yield(out, err) if block_given?
40
+ end
41
+ after_execute.changed_and_notify(self, command, last_status)
42
+ last_status
43
+ end
44
+
45
+ end
@@ -0,0 +1,45 @@
1
+ require 'session'
2
+ require "remote_exec/base"
3
+
4
+ # Class to run local commands and transfer files localy.
5
+ class RemoteExec::Local < RemoteExec::Base
6
+ # name of the shell to run
7
+ attr_reader :shell
8
+
9
+ # Constructs a new Local object.
10
+ #
11
+ # @param shell [String] name of the shell to run
12
+ # @yield [self] if a block is given then the constructed
13
+ # object yields itself and calls `#shutdown` at the end, closing the
14
+ # remote connection
15
+
16
+ def initialize(shell = "sh")
17
+ @shell = shell
18
+ super()
19
+ end
20
+
21
+ ##
22
+ # Execute command locally
23
+ #
24
+ # @param command [String] command string to execute
25
+ # @return [Integer] exit status of the command
26
+
27
+ def execute(command)
28
+ before_execute.changed_and_notify(self, command)
29
+ shell_session.execute(command) do |out,err|
30
+ on_execute_data.changed_and_notify(self, out, err)
31
+ yield(out, err) if block_given?
32
+ end
33
+ last_status = shell_session.status
34
+ after_execute.changed_and_notify(self, command, last_status)
35
+ last_status
36
+ end
37
+
38
+ private
39
+
40
+ def shell_session
41
+ @shell_session ||= Session::Sh.new(:prog => shell).tap do |shell|
42
+ after_connect.changed_and_notify(self)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,139 @@
1
+ =begin
2
+ Copyright 2014 Michal Papis <mpapis@gmail.com>
3
+
4
+ See the file LICENSE for copying permission.
5
+
6
+ Partially based on test-kitchen by Fletcher Nichol <fnichol@nichol.ca>
7
+ License: https://github.com/test-kitchen/test-kitchen/blob/459238b88c/LICENSE
8
+ =end
9
+
10
+ require 'net/ssh'
11
+ require 'ruby/hooks'
12
+ require "remote_exec/base"
13
+
14
+ # Class to help establish SSH connections, issue remote commands, and
15
+ # transfer files between a local system and remote node.
16
+ class RemoteExec::Ssh < RemoteExec::Base
17
+ # hostname for the connection
18
+ attr_reader :hostname
19
+ # username for the connection
20
+ attr_reader :username
21
+ # options for the connection
22
+ attr_accessor :options
23
+
24
+ # Constructs a new Ssh object.
25
+ #
26
+ # @param hostname [String] the remote hostname (IP address, FQDN, etc.)
27
+ # @param username [String] the username for the remote host
28
+ # @param options [Hash] configuration options for ssh
29
+ # @yield [self] if a block is given then the constructed
30
+ # object yields itself and calls `#shutdown` at the end, closing the
31
+ # remote connection
32
+ def initialize(hostname, username, options = {})
33
+ @hostname = hostname
34
+ @username = username
35
+ @options = options
36
+ super()
37
+ end
38
+
39
+ # Shuts down the session connection, if it is still active.
40
+ def shutdown
41
+ super
42
+ return if @ssh.nil?
43
+ ssh.shutdown!
44
+ ensure
45
+ @ssh = nil
46
+ end
47
+
48
+ ##
49
+ # Execute command on remote host
50
+ #
51
+ # @param command [String] command string to execute
52
+ # @return [Integer] exit status of the command
53
+
54
+ def execute(command)
55
+ # TODO: make it run in one session
56
+ @last_status = nil
57
+ @command = command
58
+ ssh.open_channel(&method(:execute_open_channel))
59
+ ssh.loop
60
+ @last_status
61
+ end
62
+
63
+ private
64
+
65
+ def execute_open_channel(channel)
66
+ before_execute.changed_and_notify(self, @command)
67
+ channel.request_pty
68
+ channel.exec(@command, &method(:execute_channel_exec))
69
+ channel.wait
70
+ after_execute.changed_and_notify(self, @command, @last_status)
71
+ end
72
+
73
+ def execute_channel_exec(channel, success)
74
+ channel.on_data(&method(:execute_on_stdout))
75
+ channel.on_extended_data(&method(:execute_on_stderr))
76
+ channel.on_request("exit-status") do |channel, data|
77
+ @last_status = data.read_long
78
+ end
79
+ end
80
+
81
+ def execute_on_stdout(channel, data)
82
+ on_execute_data.changed_and_notify(self, data, nil)
83
+ yield(data, nil) if block_given?
84
+ end
85
+
86
+ def execute_on_stderr(channel, type, data)
87
+ case type
88
+ when 1
89
+ on_execute_data.changed_and_notify(self, nil, data)
90
+ yield(nil, data) if block_given?
91
+ else
92
+ raise "Unsupported SSH extended_data type: #{type.inspect}"
93
+ end
94
+ end
95
+
96
+ def ssh
97
+ @ssh ||= establish_connection
98
+ end
99
+
100
+ RESCUE_EXCEPTIONS = [
101
+ Errno::EACCES,
102
+ Errno::EADDRINUSE,
103
+ Errno::ECONNREFUSED,
104
+ Errno::ECONNRESET,
105
+ Errno::ENETUNREACH,
106
+ Errno::EHOSTUNREACH,
107
+ Net::SSH::Disconnect,
108
+ ]
109
+
110
+ # Establish a connection session to the remote host.
111
+ #
112
+ # @return [Net::SSH::Connection::Session] the SSH connection session
113
+ # @api private
114
+ def establish_connection
115
+ @retries = options[:ssh_retries] || 2
116
+ begin
117
+ before_connect.changed_and_notify(self)
118
+ ssh = Net::SSH.start(hostname, username, options)
119
+ rescue *RESCUE_EXCEPTIONS => exception
120
+ handle_exception_retry(exception)
121
+ retry
122
+ end
123
+ after_connect.changed_and_notify(self)
124
+ ssh
125
+ end
126
+
127
+ def handle_exception_retry(exception)
128
+ if @retries > 0
129
+ on_connect_retry.changed_and_notify(self, exception, @retries)
130
+ sleep options[:ssh_timeout] || 1
131
+ @retries -= 1
132
+ else
133
+ on_connect_fail.changed_and_notify(self, exception)
134
+ # TODO: should we wrap the error in some other common class?
135
+ raise exception
136
+ end
137
+ end
138
+
139
+ end
@@ -0,0 +1,5 @@
1
+ # Unified handling of remote execution and file transfer
2
+ module RemoteExec
3
+ # Version of this gem
4
+ VERSION = '0.5.0'
5
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+ require 'remote_exec/fake'
3
+
4
+ describe RemoteExec::Fake do
5
+ subject do
6
+ RemoteExec::Fake.new
7
+ end
8
+
9
+ it "runs true" do
10
+ test_command = "true"
11
+ subject.story = [0,[]]
12
+ called = 0
13
+ status =
14
+ subject.execute(test_command) do |out, err|
15
+ called+=1
16
+ end
17
+ called.must_equal(0)
18
+ status.must_equal(0)
19
+ end
20
+
21
+ it "runs false" do
22
+ test_command = "false"
23
+ subject.story = [1,[]]
24
+ called = 0
25
+ status =
26
+ subject.execute(test_command) do |out, err|
27
+ called+=1
28
+ end
29
+ called.must_equal(0)
30
+ status.must_equal(1)
31
+ end
32
+
33
+ it "runs echo test" do
34
+ test_command = "echo test"
35
+ subject.story = [0,[["test\n",nil]]]
36
+ called = 0
37
+ status =
38
+ subject.execute(test_command) do |out, err|
39
+ out.must_equal "test\n"
40
+ err.must_be_nil
41
+ called+=1
42
+ end
43
+ called.must_equal(1)
44
+ status.must_equal(0)
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+ require 'remote_exec/local'
3
+
4
+ describe RemoteExec::Local do
5
+ subject do
6
+ RemoteExec::Local.new
7
+ end
8
+
9
+ it "runs true" do
10
+ subject.execute("true").must_equal(0)
11
+ end
12
+
13
+ it "runs false" do
14
+ subject.execute("false").must_equal(1)
15
+ end
16
+
17
+ it "runs echo test" do
18
+ test_command = "echo test"
19
+ @called = 0
20
+ status =
21
+ subject.execute(test_command) do |out, err|
22
+ assert_equal out.strip, "test"
23
+ assert_equal err, nil
24
+ @called+=1
25
+ end
26
+ @called.must_equal(1)
27
+ status.must_equal(0)
28
+ end
29
+ end
@@ -0,0 +1,271 @@
1
+ =begin
2
+ Copyright 2014 Michal Papis <mpapis@gmail.com>
3
+
4
+ See the file LICENSE for copying permission.
5
+
6
+ Partially based on test-kitchen by Fletcher Nichol <fnichol@nichol.ca>
7
+ License: https://github.com/test-kitchen/test-kitchen/blob/459238b88c/LICENSE
8
+ =end
9
+
10
+ require 'test_helper'
11
+ require 'remote_exec/ssh'
12
+ require 'net/ssh/test'
13
+
14
+ module Net
15
+ module SSH
16
+ module Test
17
+ class Channel
18
+
19
+ def sends_request_pty
20
+ pty_data = ["xterm", 80, 24, 640, 480, "\0"]
21
+ script.events << Class.new(Net::SSH::Test::LocalPacket) do
22
+ def types
23
+ if
24
+ @type == 98 && @data[1] == "pty-req"
25
+ then
26
+ @types ||= [
27
+ :long, :string, :bool, :string,
28
+ :long, :long, :long, :long, :string
29
+ ]
30
+ else
31
+ super
32
+ end
33
+ end
34
+ end.new(:channel_request, remote_id, "pty-req", false, *pty_data)
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ class ErrorCounter
43
+ attr_reader :errors
44
+ def on_error(method, *args)
45
+ @errors ||= {}
46
+ @errors[method] ||= 0
47
+ @errors[method] += 1
48
+ end
49
+ def on_connect_retry(*args)
50
+ on_error(:on_connect_retry, *args)
51
+ end
52
+ def on_connect_fail(*args)
53
+ on_error(:on_connect_fail, *args)
54
+ end
55
+ end
56
+
57
+ class ExecutaDataHook < Struct.new(:object, :stdout, :stderr)
58
+ attr_reader :results
59
+ def initialize(*args)
60
+ @results = []
61
+ super
62
+ end
63
+ def update(*args)
64
+ @results << self.class.new(*args)
65
+ end
66
+ end
67
+
68
+ describe RemoteExec::Ssh do
69
+ include Net::SSH::Test
70
+
71
+ subject do
72
+ RemoteExec::Ssh.allocate.tap do |ssh|
73
+ ssh.instance_variable_set(:@ssh, connection)
74
+ ssh.instance_variable_set(:@options, {})
75
+ end
76
+ end
77
+
78
+ describe "#initialize" do
79
+
80
+ it "sets default variables" do
81
+ subject.send(:initialize, 1, 2, 3)
82
+ subject.hostname.must_equal 1
83
+ subject.username.must_equal 2
84
+ subject.options.must_equal 3
85
+ end
86
+
87
+ it "executes initialize block once" do
88
+ calls = 0
89
+ subject.send(:initialize, 1, 2, 3) { calls+=1 }
90
+ calls.must_equal 1
91
+ end
92
+
93
+ end #initialize
94
+
95
+ describe "#execute" do
96
+ let(:hook) { ExecutaDataHook.new }
97
+
98
+ it "executes true" do
99
+ story do |session|
100
+ channel = session.opens_channel
101
+ channel.sends_request_pty
102
+ channel.sends_exec "true"
103
+ channel.gets_exit_status(0)
104
+ channel.gets_close
105
+ channel.sends_close
106
+ end
107
+
108
+ assert_scripted do
109
+ subject.execute("true").must_equal 0
110
+ end
111
+ end
112
+
113
+ it "executes false" do
114
+ story do |session|
115
+ channel = session.opens_channel
116
+ channel.sends_request_pty
117
+ channel.sends_exec "false"
118
+ channel.gets_exit_status(1)
119
+ channel.gets_close
120
+ channel.sends_close
121
+ end
122
+
123
+ assert_scripted do
124
+ subject.execute("false").must_equal 1
125
+ end
126
+ end
127
+
128
+ it "executes echo test" do
129
+ story do |session|
130
+ channel = session.opens_channel
131
+ channel.sends_request_pty
132
+ channel.sends_exec "echo test me"
133
+ channel.gets_data("test me\n")
134
+ channel.gets_exit_status(0)
135
+ channel.gets_close
136
+ channel.sends_close
137
+ end
138
+
139
+ assert_scripted do
140
+ subject.on_execute_data.add_observer(hook, :update)
141
+ subject.execute("echo test me") do |out, err|
142
+ out.must_equal "test me\n"
143
+ err.must_be_nil
144
+ end.must_equal 0
145
+ hook.results.must_equal([ExecutaDataHook.new(subject, "test me\n", nil)])
146
+ end
147
+ end
148
+
149
+ it "executes echo test>&2" do
150
+ story do |session|
151
+ channel = session.opens_channel
152
+ channel.sends_request_pty
153
+ channel.sends_exec "echo test me>&2"
154
+ channel.gets_extended_data("test me\n")
155
+ channel.gets_exit_status(0)
156
+ channel.gets_close
157
+ channel.sends_close
158
+ end
159
+
160
+ assert_scripted do
161
+ subject.on_execute_data.add_observer(hook, :update)
162
+ subject.execute("echo test me>&2") do |out, err|
163
+ out.must_be_nil
164
+ err.must_equal "test me\n"
165
+ end.must_equal 0
166
+ hook.results.must_equal([ExecutaDataHook.new(subject, nil, "test me\n")])
167
+ end
168
+ end
169
+
170
+ end #execute
171
+
172
+ describe "#execute methods" do
173
+ let(:hook) { ExecutaDataHook.new }
174
+
175
+ it "handles stdout" do
176
+ subject.on_execute_data.add_observer(hook, :update)
177
+ subject.send(:execute_on_stdout, :channel, "some text") do |stdout, stderr|
178
+ stdout.must_equal("some text")
179
+ stderr.must_be_nil
180
+ end
181
+ hook.results.must_equal([ExecutaDataHook.new(subject, "some text", nil)])
182
+ end
183
+
184
+ it "handles stderr" do
185
+ subject.on_execute_data.add_observer(hook, :update)
186
+ subject.send(:execute_on_stderr, :channel, 1, "some text") do |stdout, stderr|
187
+ stdout.must_be_nil
188
+ stderr.must_equal("some text")
189
+ end
190
+ hook.results.must_equal([ExecutaDataHook.new(subject, nil, "some text")])
191
+ end
192
+
193
+ it "does not handle extended data other then stderr" do
194
+ subject.on_execute_data.add_observer(hook, :update)
195
+ lambda {
196
+ subject.send(:execute_on_stderr, :channel, 666, "some text")
197
+ }.must_raise(RuntimeError, "Unsupported SSH extended_data type: 666")
198
+ hook.results.must_be_empty
199
+ end
200
+
201
+ end #execute methods
202
+
203
+ describe "#establish_connection" do
204
+ it "does connect" do
205
+ Net::SSH.unstub(:start)
206
+ Net::SSH.stubs(:start).returns(connection)
207
+ subject.send(:establish_connection).must_equal(connection)
208
+ end
209
+ end
210
+
211
+ describe "exception in establishing connection" do
212
+
213
+ [
214
+ Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED,
215
+ Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH,
216
+ Net::SSH::Disconnect
217
+ ].each do |klass|
218
+ describe "raising #{klass}" do
219
+
220
+ before do
221
+ @error_counter = ErrorCounter.new
222
+ Net::SSH.unstub(:start)
223
+ Net::SSH.stubs(:start).raises(klass)
224
+ subject.instance_variable_set(:@ssh, nil)
225
+ subject.options[:ssh_retries] = 2
226
+ end
227
+
228
+ it "reraises the #{klass} exception" do
229
+ subject.stubs(:sleep)
230
+ proc { subject.send(:establish_connection) }.must_raise klass
231
+ end
232
+
233
+ it "sleeps for 1 second between retries" do
234
+ subject.expects(:sleep).with(1).twice
235
+ begin
236
+ subject.send(:establish_connection)
237
+ rescue
238
+ end
239
+ end
240
+
241
+ it "calls hooks on retry/fail ':ssh_retries' times" do
242
+ subject.stubs(:sleep)
243
+ subject.on_connect_retry.add_observer(@error_counter, :on_connect_retry)
244
+ subject.on_connect_fail.add_observer(@error_counter, :on_connect_fail)
245
+ begin
246
+ subject.send(:establish_connection)
247
+ rescue
248
+ end
249
+ @error_counter.errors.must_equal({:on_connect_retry=>2, :on_connect_fail=>1})
250
+ end
251
+
252
+ end
253
+ end
254
+
255
+ end #"exception in establishing connection"
256
+
257
+ describe "#handle_exception_retry" do
258
+ it "does decrease reties count" do
259
+ subject.instance_variable_set(:@retries, 2)
260
+ subject.send(:handle_exception_retry, "exception_test")
261
+ subject.instance_variable_get(:@retries).must_equal(1)
262
+ subject.send(:handle_exception_retry, "exception_test")
263
+ subject.instance_variable_get(:@retries).must_equal(0)
264
+ lambda {
265
+ subject.send(:handle_exception_retry, "exception_test")
266
+ }.must_raise(RuntimeError, "exception_test")
267
+ subject.instance_variable_get(:@retries).must_equal(0)
268
+ end
269
+ end
270
+
271
+ end
@@ -0,0 +1,34 @@
1
+ =begin
2
+ Copyright 2014 Michal Papis <mpapis@gmail.com>
3
+
4
+ See the file LICENSE for copying permission.
5
+ =end
6
+
7
+ require "rubygems"
8
+ gem "minitest"
9
+
10
+ if
11
+ RUBY_VERSION == "2.0.0" && # check Gemfile
12
+ $0 != "-e" # do not do that in guard
13
+ then
14
+ require "coveralls"
15
+ require "simplecov"
16
+
17
+ SimpleCov.start do
18
+ formatter SimpleCov::Formatter::MultiFormatter[
19
+ SimpleCov::Formatter::HTMLFormatter,
20
+ Coveralls::SimpleCov::Formatter,
21
+ ]
22
+ command_name "Unit Tests"
23
+ add_filter "/test/"
24
+ end
25
+
26
+ Coveralls.noisy = true unless ENV["CI"]
27
+ end
28
+
29
+ # Autoload all lib/**/*.rb files so simplecov does not misses anything
30
+ Dir[File.expand_path("../../lib/**/*.rb", __FILE__)].each{|f| require f }
31
+
32
+ require "minitest/autorun" unless $0=="-e" # skip in guard
33
+ require "minitest/unit"
34
+ require "mocha/setup"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remote-exec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Papis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-04 00:00:00.000000000 Z
11
+ date: 2014-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ruby-hooks
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: session
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,34 @@ dependencies:
38
52
  - - '>='
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: guard-minitest
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: guard-yard
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'
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: rake
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +94,34 @@ dependencies:
52
94
  - - '>='
53
95
  - !ruby/object:Gem::Version
54
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: mocha
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
55
125
  description: Invoke commands on remote hosts
56
126
  email:
57
127
  - mpapis@gmail.com
@@ -59,10 +129,16 @@ executables: []
59
129
  extensions: []
60
130
  extra_rdoc_files: []
61
131
  files:
62
- - lib/remote/exec/fake.rb
63
- - lib/remote/exec/local.rb
64
- - lib/remote/exec/ssh.rb
65
- - lib/remote/exec/version.rb
132
+ - lib/remote-exec.rb
133
+ - lib/remote_exec/base.rb
134
+ - lib/remote_exec/fake.rb
135
+ - lib/remote_exec/local.rb
136
+ - lib/remote_exec/ssh.rb
137
+ - lib/remote_exec/version.rb
138
+ - test/remote_exec/fake_test.rb
139
+ - test/remote_exec/local_test.rb
140
+ - test/remote_exec/ssh_test.rb
141
+ - test/test_helper.rb
66
142
  homepage: https://github.com/remote-exec/remote-exec
67
143
  licenses:
68
144
  - MIT
@@ -83,9 +159,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
159
  version: '0'
84
160
  requirements: []
85
161
  rubyforge_project:
86
- rubygems_version: 2.4.2
162
+ rubygems_version: 2.2.2
87
163
  signing_key:
88
164
  specification_version: 4
89
165
  summary: Invoke commands on remote hosts
90
- test_files: []
166
+ test_files:
167
+ - test/test_helper.rb
168
+ - test/remote_exec/local_test.rb
169
+ - test/remote_exec/ssh_test.rb
170
+ - test/remote_exec/fake_test.rb
91
171
  has_rdoc:
@@ -1,15 +0,0 @@
1
- class Remote::Exec::Fake
2
- attr_reader :last_status
3
-
4
- def execute(command)
5
- @last_status, outputs = @respond.call(command)
6
- outputs.each do |out, err|
7
- yield(out, err)
8
- end
9
- @last_status
10
- end
11
-
12
- def respond(&block)
13
- @respond = block
14
- end
15
- end
@@ -1,21 +0,0 @@
1
- require 'session'
2
-
3
- class Remote::Exec::Local
4
- attr_reader :shell, :last_status
5
-
6
- def initialize(shell = "sh")
7
- @shell = shell
8
- end
9
-
10
- def execute(command)
11
- @last_status = nil
12
- shell_session.execute(command) do |out, err|
13
- yield(out, err)
14
- end
15
- @last_status = shell_session.status
16
- end
17
-
18
- def shell_session
19
- @shell_session ||= Session::Sh.new(:prog => shell)
20
- end
21
- end
@@ -1,36 +0,0 @@
1
- require 'net/ssh'
2
-
3
- class Remote::Exec::Ssh
4
- attr_reader :host, :user, :last_status
5
-
6
- def initialize(host, user = nil)
7
- @host = host
8
- @user = user
9
- end
10
-
11
- # TODO: make it run in one session
12
- def execute(command)
13
- @last_status = nil
14
- ssh.open_channel do |channel|
15
- channel.request_pty
16
- channel.exec command do |ch, success|
17
- channel.on_data do |ch, data|
18
- yield(data, nil)
19
- end
20
- channel.on_extended_data do |ch, type, data|
21
- yield(nil, data)
22
- end
23
- channel.on_request("exit-status") do |ch, data|
24
- @last_status = data.read_long
25
- end
26
- end
27
- channel.wait
28
- end
29
- ssh.loop
30
- @last_status
31
- end
32
-
33
- def ssh
34
- @ssh ||= Net::SSH.start(host, user)
35
- end
36
- end
@@ -1,6 +0,0 @@
1
- module Remote
2
- class Exec
3
- VERSION = '0.0.1'
4
- end
5
- end
6
-