gofer 0.3.0 → 0.3.1
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 +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
|