gofer 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/{HISTORY.md → CHANGELOG.md} +18 -4
- data/README.md +17 -20
- data/lib/gofer/host.rb +4 -1
- data/lib/gofer/ssh_wrapper.rb +14 -2
- data/lib/gofer/version.rb +1 -1
- data/spec/gofer/integration_spec.rb +53 -23
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
ZWNlZjlhNDIyYjAyMDAxMWMwNTRiMmFlN2ZjMjk4NGYzYmU4NDRlMQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YzA2ZmJkNGUzY2U5NmQwN2I4YTRmODIwMmY5OTJiOTk1ZmZjNjdmZQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZTBmYWFhOGEwODAzMzZhNGVhNWJmOTMyNDQ0N2FjMDc5ZjkwZjI3OWU2YmQx
|
10
|
+
ZjZkYTZlZmQ3MWYyMGE0MWFlM2FkODFjNTI1ZjE0MGMzYjY3MTEwMzc5ZGFk
|
11
|
+
OGQyYTZkZmE0OWQ3NjgzN2ExYmIxMTBkZjliZDE2YzM2ZWNmMTQ=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
MzU4MGRhMmM1ZWNlNWE4MWFiNzZkMmViZjk1MTNiYjJmMDI1ZmZjODM4NGM3
|
14
|
+
NGM3YjNhOTUzYmEyYjEwYjRiZGYyN2E2ZWRmM2E0YWEyMzdiOTQ1NTYyMWUz
|
15
|
+
M2Q4M2U4M2Q0YmVlYmZhYTVmMDJkMzA1MGUyYTMyNzM3YzE5NjM=
|
data/{HISTORY.md → CHANGELOG.md}
RENAMED
@@ -1,4 +1,18 @@
|
|
1
1
|
# Revision History
|
2
|
+
|
3
|
+
### v0.3.1
|
4
|
+
|
5
|
+
* Prefix stderr/stdout per host with `:output_prefix`
|
6
|
+
|
7
|
+
### v0.3.0
|
8
|
+
|
9
|
+
* Add cluster support via `Gofer::Cluster` (@rich0h)
|
10
|
+
|
11
|
+
### v0.2.6 30/08/2012
|
12
|
+
|
13
|
+
* Preserve options on file upload/download
|
14
|
+
* Include host & server response in `Gofer::HostError` exceptions
|
15
|
+
|
2
16
|
### v0.2.5 02/06/2011
|
3
17
|
|
4
18
|
* `#exists?` -> `#exist?` to be consistent with `File.exist?`
|
@@ -6,7 +20,7 @@
|
|
6
20
|
### v0.2.4 24/05/2011
|
7
21
|
|
8
22
|
* Add `:quiet` as an option on `Gofer::Host` instantiation
|
9
|
-
|
23
|
+
|
10
24
|
### v0.2.3 21/05/2011
|
11
25
|
|
12
26
|
* Add `write` command to `Gofer::Host`
|
@@ -14,7 +28,7 @@
|
|
14
28
|
### v0.2.2 10/05/20011
|
15
29
|
|
16
30
|
* Add `run_multiple` method to `Gofer::Host`
|
17
|
-
|
31
|
+
|
18
32
|
### v0.2.1 08/05/2011
|
19
33
|
|
20
34
|
* Add `quiet=` to Host instance to allow setting quiet to be the default.
|
@@ -22,7 +36,7 @@
|
|
22
36
|
### v0.2.0 03/05/2011
|
23
37
|
|
24
38
|
* Flip ordering of username/hostname on instantiation to match that of `Net::SSH`
|
25
|
-
|
39
|
+
|
26
40
|
### v0.1.2 03/05/2011
|
27
41
|
|
28
42
|
* Pass through `Gofer::Host` instantiation options straight through to `Net::SSH`.
|
@@ -35,7 +49,7 @@
|
|
35
49
|
|
36
50
|
* Replace string return from run with a 'response' object
|
37
51
|
* Removed 'within' functionality - will be replaced by 'open' later
|
38
|
-
|
52
|
+
|
39
53
|
### v0.0.1 03/04/2011
|
40
54
|
|
41
55
|
* Initial release
|
data/README.md
CHANGED
@@ -61,6 +61,13 @@ puts response.stderr # will print "goodbye\n"
|
|
61
61
|
puts response.output # will print "hello\ngoodbye\n"
|
62
62
|
```
|
63
63
|
|
64
|
+
### Prefix output
|
65
|
+
|
66
|
+
``` ruby
|
67
|
+
h.output_prefix = 'apollo' # or set :output_prefix on instantiation
|
68
|
+
h.run "echo hello; echo goodbye" # prints apollo: hello\napollo: goodbye
|
69
|
+
```
|
70
|
+
|
64
71
|
### Suppress output
|
65
72
|
|
66
73
|
``` ruby
|
@@ -80,8 +87,8 @@ puts response.stdout # will print "hello\ngoodbye\n"
|
|
80
87
|
|
81
88
|
``` ruby
|
82
89
|
cluster = Gopher::Cluster.new
|
83
|
-
cluster << Gofer::Host.new('my.host.com', 'ubuntu', :keys => ['key.pem'])
|
84
|
-
cluster << Gofer::Host.new('other.host.com', 'ubuntu', :keys => ['key.pem'])
|
90
|
+
cluster << Gofer::Host.new('my.host.com', 'ubuntu', :keys => ['key.pem'], :output_prefix => " my")
|
91
|
+
cluster << Gofer::Host.new('other.host.com', 'ubuntu', :keys => ['key.pem'], :output_prefix => "other")
|
85
92
|
|
86
93
|
cluster.run do |c|
|
87
94
|
c.run("hostname") # This will run on both hosts at once
|
@@ -92,34 +99,24 @@ cluster.run(:max_concurrency => 1) do |c|
|
|
92
99
|
end
|
93
100
|
```
|
94
101
|
|
95
|
-
## Planned Features
|
96
|
-
|
97
|
-
``` ruby
|
98
|
-
# constant connection (no reconnect for each action)
|
99
|
-
Gofer::Host.new(...).open do |h|
|
100
|
-
h.run( ... )
|
101
|
-
end
|
102
|
-
|
103
|
-
# overriding defaults
|
104
|
-
h.set :quiet => true
|
105
|
-
h.set :capture_exit_status => false
|
106
|
-
|
107
|
-
# Local system usage, too:
|
108
|
-
Gofer::Localhost.new.run "hostname" # > my.macbook.com
|
109
|
-
```
|
110
|
-
|
111
102
|
## Testing
|
112
103
|
|
113
104
|
* Ensure that your user can ssh as itself to localhost using the key in `~/.ssh/id_rsa`.
|
114
105
|
* Run `rspec spec` or `bundle install && rake spec`
|
115
106
|
|
107
|
+
## Contributing
|
108
|
+
|
109
|
+
Contributions should be via pull request. Please add tests and a note in the
|
110
|
+
`README.md` for new functionality. Please use 1.8.7-compatiable syntax.
|
111
|
+
|
116
112
|
## TODO
|
117
113
|
|
118
114
|
* ls, exists?, directory? should use sftp if available rather than shell commands
|
119
|
-
* wrap STDOUT with host prefix for easy identification of system output
|
120
115
|
* Deal with timeouts/disconnects on persistent connections
|
121
|
-
* CHANGELOG.md
|
122
116
|
* Release 1.0 & use Semver
|
117
|
+
* Ensure RDodc is complete & up to date, link to rdoc.info from README
|
118
|
+
* Add unit tests, bring in Travis.ci
|
119
|
+
* Local system usage (eg `Gofer::Localhost.new.run "hostname"`)
|
123
120
|
|
124
121
|
## License
|
125
122
|
|
data/lib/gofer/host.rb
CHANGED
@@ -13,13 +13,14 @@ module Gofer
|
|
13
13
|
class Host
|
14
14
|
|
15
15
|
attr_reader :hostname
|
16
|
-
attr_accessor :quiet
|
16
|
+
attr_accessor :quiet, :output_prefix
|
17
17
|
|
18
18
|
# Create a new Host connection
|
19
19
|
#
|
20
20
|
# Options:
|
21
21
|
#
|
22
22
|
# +quiet+:: Don't print stdout output from +run+ commands
|
23
|
+
# +output_prefix+:: Prefix each line of stdout to differentiate multiple host output
|
23
24
|
# All other+opts+ is passed through directly to Net::SSH.start
|
24
25
|
# See http://net-ssh.github.com/ssh/v2/api/index.html for valid arguments.
|
25
26
|
def initialize _hostname, username, opts={}
|
@@ -31,6 +32,7 @@ module Gofer
|
|
31
32
|
end
|
32
33
|
|
33
34
|
@quiet = opts.delete(:quiet)
|
35
|
+
@output_prefix = opts.delete(:output_prefix)
|
34
36
|
|
35
37
|
# support legacy identity_file argument
|
36
38
|
if opts[:identity_file]
|
@@ -55,6 +57,7 @@ module Gofer
|
|
55
57
|
# +capture_exit_status+:: Don't raise an error on a non-zero exit status
|
56
58
|
def run command, opts={}
|
57
59
|
opts[:quiet] = quiet unless opts.include?(:quiet)
|
60
|
+
opts[:output_prefix] = @output_prefix
|
58
61
|
response = @ssh.run command, opts
|
59
62
|
if !opts[:capture_exit_status] && response.exit_status != 0
|
60
63
|
raise HostError.new(self, response, "Command #{command} failed with exit status #{@ssh.last_exit_status}")
|
data/lib/gofer/ssh_wrapper.rb
CHANGED
@@ -8,6 +8,7 @@ module Gofer
|
|
8
8
|
|
9
9
|
def initialize *args
|
10
10
|
@net_ssh_args = args
|
11
|
+
@at_start_of_line = true
|
11
12
|
end
|
12
13
|
|
13
14
|
def run command, opts={}
|
@@ -49,14 +50,14 @@ module Gofer
|
|
49
50
|
channel.on_data do |ch, data| # stdout
|
50
51
|
stdout += data
|
51
52
|
output += data
|
52
|
-
$stdout.print data unless opts[:quiet]
|
53
|
+
$stdout.print wrap_output(data, opts[:output_prefix]) unless opts[:quiet]
|
53
54
|
end
|
54
55
|
|
55
56
|
channel.on_extended_data do |ch, type, data|
|
56
57
|
next unless type == 1 # only handle stderr
|
57
58
|
stderr += data
|
58
59
|
output += data
|
59
|
-
$stderr.print data unless opts[:quiet_stderr]
|
60
|
+
$stderr.print wrap_output(data, opts[:output_prefix]) unless opts[:quiet_stderr]
|
60
61
|
end
|
61
62
|
|
62
63
|
channel.on_request("exit-status") do |ch, data|
|
@@ -70,5 +71,16 @@ module Gofer
|
|
70
71
|
ssh.loop
|
71
72
|
Gofer::Response.new(stdout, stderr, output, exit_code)
|
72
73
|
end
|
74
|
+
|
75
|
+
def wrap_output output, prefix
|
76
|
+
return output unless prefix
|
77
|
+
|
78
|
+
output = "#{prefix}: " + output if @at_start_of_line
|
79
|
+
|
80
|
+
@at_start_of_line = output.end_with?("\n")
|
81
|
+
|
82
|
+
output.gsub(/\n(.)/, "\n#{prefix}: \\1")
|
83
|
+
end
|
84
|
+
|
73
85
|
end
|
74
86
|
end
|
data/lib/gofer/version.rb
CHANGED
@@ -29,6 +29,14 @@ describe Gofer do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
def with_captured_output
|
33
|
+
@stdout = ''
|
34
|
+
@stderr = ''
|
35
|
+
@combined = ''
|
36
|
+
$stdout.stub!( :write ) { |*args| @stdout.<<( *args ); @combined.<<( *args )}
|
37
|
+
$stderr.stub!( :write ) { |*args| @stderr.<<( *args ); @combined.<<( *args )}
|
38
|
+
end
|
39
|
+
|
32
40
|
before :all do
|
33
41
|
@host = Gofer::Host.new(HOSTNAME, USERNAME, :keys => [IDENTITY_FILE], :quiet => true)
|
34
42
|
@tmpdir = raw_ssh("mktemp -d /tmp/gofertest.XXXXX").chomp
|
@@ -86,6 +94,35 @@ describe Gofer do
|
|
86
94
|
it_should_behave_like "an output capturer"
|
87
95
|
end
|
88
96
|
|
97
|
+
|
98
|
+
it "should print stdout responses if quiet is false" do
|
99
|
+
$stdout.should_receive(:write).with "stdout\n"
|
100
|
+
@host.run "echo stdout", :quiet => false
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should print stderr responses if quiet_stderr is false" do
|
104
|
+
$stderr.should_receive(:write).with "stderr\n"
|
105
|
+
@host.run "echo stderr 1>&2", :quiet_stderr => false
|
106
|
+
end
|
107
|
+
|
108
|
+
context "with a host output prefix" do
|
109
|
+
before do
|
110
|
+
@host.output_prefix = "derp"
|
111
|
+
with_captured_output
|
112
|
+
end
|
113
|
+
it "should prefix each line of the stdout and stderr responses with the output prefix" do
|
114
|
+
@host.run "echo stdout; echo stdout2; echo stderr 1>&2; echo stderr2 1>&2", :quiet => false, :quiet_stderr => false
|
115
|
+
@stdout.should eq "derp: stdout\nderp: stdout2\n"
|
116
|
+
@stderr.should eq "derp: stderr\nderp: stderr2\n"
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should not prefix if the output is not actually on a new line" do
|
120
|
+
@host.run "echo -n foo; echo bar; echo baz; ", :quiet => false
|
121
|
+
@combined.should eq "derp: foobar\nderp: baz\n"
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
89
126
|
it "should error if a command returns a non-zero response" do
|
90
127
|
lambda {@host.run "false"}.should raise_error(/failed with exit status/)
|
91
128
|
begin
|
@@ -204,42 +241,35 @@ describe Gofer do
|
|
204
241
|
end
|
205
242
|
|
206
243
|
describe :cluster do
|
207
|
-
|
208
|
-
cluster = Gofer::Cluster.new
|
244
|
+
before do
|
245
|
+
@cluster = Gofer::Cluster.new
|
209
246
|
# Cheat and use the same host repeatedly
|
210
|
-
host1 = Gofer::Host.new(HOSTNAME, USERNAME, :keys => [IDENTITY_FILE], :quiet => true)
|
211
|
-
host2 = Gofer::Host.new(HOSTNAME, USERNAME, :keys => [IDENTITY_FILE], :quiet => true)
|
212
|
-
cluster << host1
|
213
|
-
cluster << host2
|
247
|
+
@host1 = Gofer::Host.new(HOSTNAME, USERNAME, :keys => [IDENTITY_FILE], :quiet => true)
|
248
|
+
@host2 = Gofer::Host.new(HOSTNAME, USERNAME, :keys => [IDENTITY_FILE], :quiet => true)
|
249
|
+
@cluster << @host1
|
250
|
+
@cluster << @host2
|
251
|
+
end
|
214
252
|
|
215
|
-
|
216
|
-
|
253
|
+
it "should run commands in parallel" do
|
254
|
+
results = @cluster.run do |c|
|
255
|
+
c.run "ruby -e 'puts Time.now.to_f; sleep 0.1; puts Time.now.to_f'"
|
217
256
|
end
|
218
257
|
|
219
|
-
res1 = results[host1].stdout.lines.to_a
|
220
|
-
res2 = results[host2].stdout.lines.to_a
|
258
|
+
res1 = results[@host1].stdout.lines.to_a
|
259
|
+
res2 = results[@host2].stdout.lines.to_a
|
221
260
|
|
222
261
|
expect(res1[1].to_f).to be > res2[0].to_f
|
223
|
-
|
224
262
|
end
|
225
263
|
|
226
264
|
it "should respect max_concurrency" do
|
227
|
-
|
228
|
-
|
229
|
-
host1 = Gofer::Host.new(HOSTNAME, USERNAME, :keys => [IDENTITY_FILE], :quiet => true)
|
230
|
-
host2 = Gofer::Host.new(HOSTNAME, USERNAME, :keys => [IDENTITY_FILE], :quiet => true)
|
231
|
-
cluster << host1
|
232
|
-
cluster << host2
|
233
|
-
|
234
|
-
results = cluster.run(:max_concurrency => 1) do |c|
|
235
|
-
c.run "date '+%s%N'; sleep 1; date '+%s%N'"
|
265
|
+
results = @cluster.run(:max_concurrency => 1) do |c|
|
266
|
+
c.run "ruby -e 'puts Time.now.to_f; sleep 0.1; puts Time.now.to_f'"
|
236
267
|
end
|
237
268
|
|
238
|
-
res1 = results[host1].stdout.lines.to_a
|
239
|
-
res2 = results[host2].stdout.lines.to_a
|
269
|
+
res1 = results[@host1].stdout.lines.to_a
|
270
|
+
res2 = results[@host2].stdout.lines.to_a
|
240
271
|
|
241
272
|
expect(res2[0].to_f).to be >= res1[1].to_f
|
242
|
-
|
243
273
|
end
|
244
274
|
end
|
245
275
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gofer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Pearson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-05-
|
11
|
+
date: 2013-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: net-ssh
|
@@ -52,8 +52,8 @@ extensions: []
|
|
52
52
|
extra_rdoc_files: []
|
53
53
|
files:
|
54
54
|
- .gitignore
|
55
|
+
- CHANGELOG.md
|
55
56
|
- Gemfile
|
56
|
-
- HISTORY.md
|
57
57
|
- README.md
|
58
58
|
- Rakefile
|
59
59
|
- gofer.gemspec
|