gofer 0.5.0 → 0.6.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 +15 -0
- data/CHANGELOG.md +5 -0
- data/README.md +20 -2
- data/lib/gofer.rb +1 -0
- data/lib/gofer/cluster.rb +16 -4
- data/lib/gofer/cluster_error.rb +13 -0
- data/lib/gofer/host.rb +1 -0
- data/lib/gofer/ssh_wrapper.rb +5 -0
- data/lib/gofer/version.rb +1 -1
- data/spec/gofer/cluster_spec.rb +13 -2
- data/spec/gofer/host_spec.rb +4 -0
- metadata +31 -35
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ODJiNjE3ZGEwZmNhZmI4ZTE4ZDdjOWE5M2YzY2EyZmYxYmM3ODZmNQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
Y2E3MzlkMTliYWNlM2E4ZGRlYjFmOTU0MWYwNjYzOTUyMmZlNDkyOQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MDY1NWQ4Nzg4NGNjODFhOGU0MDk5M2NlODkyYzg2YWRhNGEyOWNkM2E2MDgz
|
10
|
+
MzQ5ZjE5MDI2MTk3Y2ZkZmRiMmFhYzc2YWNkOTE2NDY4ZGFlNWRhMjU2NzEx
|
11
|
+
ZWQ4YmZhN2ZkOGE5NzQ0YzEwMzkwNGU3OTQ4NWY4MzM2MDZhOTI=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NzBjNzdiMDQxYTZjOGJhZmIxNzAwNGY5NWMxYWM5Mjk2YTIyMGViZTQ1ZDFh
|
14
|
+
ZjQzZGRlZDdmYWNkYjc4ZDNiYmNlMDQ3NDEwZGMwZmE1NTU1Y2U2ZjU4ZThk
|
15
|
+
YWNjZjkyZjJlNDU3ODgwN2I3YzU0Nzg1ZDI4NmVmMjVjMjMzMGI=
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Gofer!
|
2
2
|
|
3
|
-
[](https://codeclimate.com/github/mipearson/gofer)
|
3
|
+
[](https://codeclimate.com/github/mipearson/gofer) [](http://badge.fury.io/rb/gofer) [](https://gemnasium.com/mipearson/gofer)
|
4
|
+
|
4
5
|
|
5
6
|
**Gofer** is a set of wrappers around the Net::SSH suite of tools to enable consistent access to remote systems.
|
6
7
|
|
@@ -66,6 +67,12 @@ puts response.stderr # will print "goodbye\n"
|
|
66
67
|
puts response.output # will print "hello\ngoodbye\n"
|
67
68
|
```
|
68
69
|
|
70
|
+
### Send input
|
71
|
+
|
72
|
+
``` ruby
|
73
|
+
h.run "sed 's/foo/bar/' 1>&2\n", :stdin => "hello foo\n" # response will be "hello bar"
|
74
|
+
```
|
75
|
+
|
69
76
|
### Prefix output
|
70
77
|
|
71
78
|
``` ruby
|
@@ -84,7 +91,7 @@ h.quiet = true # never print stdout
|
|
84
91
|
### Run the same commands on multiple hosts
|
85
92
|
|
86
93
|
``` ruby
|
87
|
-
cluster =
|
94
|
+
cluster = Gofer::Cluster.new
|
88
95
|
cluster << Gofer::Host.new('my.host.com', 'ubuntu', :keys => ['key.pem'], :output_prefix => " my")
|
89
96
|
cluster << Gofer::Host.new('other.host.com', 'ubuntu', :keys => ['key.pem'], :output_prefix => "other")
|
90
97
|
|
@@ -102,6 +109,17 @@ host.run("rake migrations")
|
|
102
109
|
# Inspect the results from each host
|
103
110
|
results = cluster.run "echo hostname"
|
104
111
|
puts results.values.join(", ") # will print "my.host.com, other.host.com"
|
112
|
+
|
113
|
+
# Capture exceptions from each host
|
114
|
+
begin
|
115
|
+
cluster.run "rake deploy"
|
116
|
+
rescue Gofer::ClusterError => e
|
117
|
+
e.errors.each do |host, exception|
|
118
|
+
$stderr.puts "Failed on #{host} with #{exception}, rolling back ..."
|
119
|
+
host.run "rake rollback"
|
120
|
+
end
|
121
|
+
raise e
|
122
|
+
end
|
105
123
|
```
|
106
124
|
|
107
125
|
## Testing
|
data/lib/gofer.rb
CHANGED
data/lib/gofer/cluster.rb
CHANGED
@@ -88,15 +88,23 @@ module Gofer
|
|
88
88
|
# +_in+ queue, and writes values to the +_out+ queue for syncronisation.
|
89
89
|
def threaded(meth, *args)
|
90
90
|
_in = run_queue
|
91
|
-
length =
|
91
|
+
length = _in.length
|
92
92
|
_out = Queue.new
|
93
93
|
results = {}
|
94
|
-
|
94
|
+
errors = {}
|
95
|
+
results_semaphore = Mutex.new
|
96
|
+
errors_semaphore = Mutex.new
|
97
|
+
concurrency.times do
|
95
98
|
Thread.new do
|
96
99
|
loop do
|
97
100
|
host = _in.pop(false) rescue Thread.exit
|
98
101
|
|
99
|
-
|
102
|
+
begin
|
103
|
+
result = host.send(meth, *args)
|
104
|
+
results_semaphore.synchronize { results[host] = result }
|
105
|
+
rescue Exception => e
|
106
|
+
errors_semaphore.synchronize { errors[host] = e }
|
107
|
+
end
|
100
108
|
_out << true
|
101
109
|
end
|
102
110
|
end
|
@@ -106,7 +114,11 @@ module Gofer
|
|
106
114
|
_out.pop
|
107
115
|
end
|
108
116
|
|
109
|
-
|
117
|
+
if errors.size > 0
|
118
|
+
raise Gofer::ClusterError.new(errors)
|
119
|
+
else
|
120
|
+
results
|
121
|
+
end
|
110
122
|
end
|
111
123
|
|
112
124
|
def run_queue
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Gofer
|
2
|
+
# Error(s) encountered performing a Gofer command on a cluster of hosts
|
3
|
+
class ClusterError < Exception
|
4
|
+
|
5
|
+
# Exception instances by host
|
6
|
+
attr_reader :errors
|
7
|
+
|
8
|
+
def initialize errors={}
|
9
|
+
@errors = errors
|
10
|
+
super errors.values.map(&:to_s).join(', ')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/gofer/host.rb
CHANGED
@@ -62,6 +62,7 @@ module Gofer
|
|
62
62
|
# +quiet+:: Don't print +stdout+, can also be set with +quiet=+ on the instance
|
63
63
|
# +quiet_stderr+:: Don't print +stderr+
|
64
64
|
# +capture_exit_status+:: Don't raise an error on a non-zero exit status
|
65
|
+
# +stdin+:: Send a string on +stdin+ then close the file
|
65
66
|
def run command, opts={}
|
66
67
|
opts[:quiet] = quiet unless opts.include?(:quiet)
|
67
68
|
opts[:output_prefix] = @output_prefix
|
data/lib/gofer/ssh_wrapper.rb
CHANGED
data/lib/gofer/version.rb
CHANGED
data/spec/gofer/cluster_spec.rb
CHANGED
@@ -15,7 +15,7 @@ describe Gofer::Cluster do
|
|
15
15
|
after(:all) { clean_tmpdir }
|
16
16
|
|
17
17
|
it "should run commands in parallel" do
|
18
|
-
results = @cluster.run("ruby -e 'puts Time.now.to_f; sleep 0.1; puts Time.now.to_f'")
|
18
|
+
results = @cluster.run("bash -l -c \"ruby -e 'puts Time.now.to_f; sleep 0.1; puts Time.now.to_f'\"")
|
19
19
|
|
20
20
|
res1 = results[@host1].stdout.lines.to_a
|
21
21
|
res2 = results[@host2].stdout.lines.to_a
|
@@ -25,7 +25,7 @@ describe Gofer::Cluster do
|
|
25
25
|
|
26
26
|
it "should respect max_concurrency" do
|
27
27
|
@cluster.max_concurrency = 1
|
28
|
-
results = @cluster.run("ruby -e 'puts Time.now.to_f; sleep 0.1; puts Time.now.to_f'")
|
28
|
+
results = @cluster.run("bash -l -c \"ruby -e 'puts Time.now.to_f; sleep 0.1; puts Time.now.to_f'\"")
|
29
29
|
|
30
30
|
res1 = results[@host1].stdout.lines.to_a
|
31
31
|
res2 = results[@host2].stdout.lines.to_a
|
@@ -33,6 +33,17 @@ describe Gofer::Cluster do
|
|
33
33
|
expect(res2[0].to_f).to be >= res1[1].to_f
|
34
34
|
end
|
35
35
|
|
36
|
+
it "should encapsulate errors in a Gofer::ClusterError container exception" do
|
37
|
+
expect { @cluster.run("false") }.to raise_error(Gofer::ClusterError)
|
38
|
+
begin
|
39
|
+
@cluster.run "false"
|
40
|
+
rescue Gofer::ClusterError => e
|
41
|
+
expect(e.errors.keys.length).to eq(2)
|
42
|
+
expect(e.errors[@host1]).to be_a(Gofer::HostError)
|
43
|
+
expect(e.errors[@host2]).to be_a(Gofer::HostError)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
36
47
|
# TODO: Make this a custom matcher?
|
37
48
|
def results_should_eq expected, &block
|
38
49
|
results = block.call
|
data/spec/gofer/host_spec.rb
CHANGED
@@ -83,6 +83,10 @@ describe Gofer::Host do
|
|
83
83
|
@combined.should eq "derp: foobar\nderp: baz\n"
|
84
84
|
end
|
85
85
|
|
86
|
+
it "should process stdin when stdin is set" do
|
87
|
+
@host.run "sed 's/foo/baz/'", :stdin => "foobar", :quiet => false
|
88
|
+
@stdout.should eq "derp: bazbar\n"
|
89
|
+
end
|
86
90
|
end
|
87
91
|
|
88
92
|
it "should error if a command returns a non-zero response" do
|
metadata
CHANGED
@@ -1,59 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gofer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
version: 0.5.0
|
4
|
+
version: 0.6.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Michael Pearson
|
9
|
-
autorequire:
|
8
|
+
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-10-13 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: net-ssh
|
16
|
-
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
17
16
|
requirements:
|
18
|
-
- -
|
17
|
+
- - ! '>='
|
19
18
|
- !ruby/object:Gem::Version
|
20
19
|
version: 2.0.23
|
21
|
-
|
22
|
-
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ! '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 2.0.23
|
27
|
-
none: false
|
28
|
-
prerelease: false
|
29
|
-
type: :runtime
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: net-scp
|
32
|
-
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
33
30
|
requirements:
|
34
|
-
- -
|
31
|
+
- - ! '>='
|
35
32
|
- !ruby/object:Gem::Version
|
36
33
|
version: 1.0.4
|
37
|
-
|
38
|
-
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
37
|
requirements:
|
40
|
-
- -
|
38
|
+
- - ! '>='
|
41
39
|
- !ruby/object:Gem::Version
|
42
40
|
version: 1.0.4
|
43
|
-
|
44
|
-
prerelease: false
|
45
|
-
type: :runtime
|
46
|
-
description: |2
|
41
|
+
description: ! '
|
47
42
|
|
48
43
|
Gofer provides a flexible and reliable model for performing tasks on remote
|
44
|
+
|
49
45
|
server using Net::SSH
|
46
|
+
|
47
|
+
'
|
50
48
|
email:
|
51
49
|
- mipearson@gmail.com
|
52
50
|
executables: []
|
53
51
|
extensions: []
|
54
52
|
extra_rdoc_files: []
|
55
53
|
files:
|
56
|
-
-
|
54
|
+
- .gitignore
|
57
55
|
- CHANGELOG.md
|
58
56
|
- Gemfile
|
59
57
|
- README.md
|
@@ -61,6 +59,7 @@ files:
|
|
61
59
|
- gofer.gemspec
|
62
60
|
- lib/gofer.rb
|
63
61
|
- lib/gofer/cluster.rb
|
62
|
+
- lib/gofer/cluster_error.rb
|
64
63
|
- lib/gofer/host.rb
|
65
64
|
- lib/gofer/host_error.rb
|
66
65
|
- lib/gofer/response.rb
|
@@ -73,29 +72,26 @@ files:
|
|
73
72
|
- test.sh
|
74
73
|
homepage: https://github.com/mipearson/gofer
|
75
74
|
licenses: []
|
76
|
-
|
75
|
+
metadata: {}
|
76
|
+
post_install_message:
|
77
77
|
rdoc_options: []
|
78
78
|
require_paths:
|
79
79
|
- lib
|
80
80
|
required_ruby_version: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
|
-
- -
|
82
|
+
- - ! '>='
|
83
83
|
- !ruby/object:Gem::Version
|
84
|
-
version:
|
85
|
-
MA==
|
86
|
-
none: false
|
84
|
+
version: '0'
|
87
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
86
|
requirements:
|
89
|
-
- -
|
87
|
+
- - ! '>='
|
90
88
|
- !ruby/object:Gem::Version
|
91
|
-
version:
|
92
|
-
MA==
|
93
|
-
none: false
|
89
|
+
version: '0'
|
94
90
|
requirements: []
|
95
|
-
rubyforge_project:
|
96
|
-
rubygems_version:
|
97
|
-
signing_key:
|
98
|
-
specification_version:
|
91
|
+
rubyforge_project:
|
92
|
+
rubygems_version: 2.0.3
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
99
95
|
summary: run commands on remote servers using SSH
|
100
96
|
test_files:
|
101
97
|
- spec/gofer/cluster_spec.rb
|