remote-exec 0.0.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
-