gofer 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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=
@@ -1,5 +1,10 @@
1
1
  # Revision History
2
2
 
3
+ ### v0.6.0
4
+
5
+ * Add `Gofer::ClusterError` to encapsulate errors encountered during clustered runs
6
+ * Add support for `stdin` option on `Gofer::Host#run`
7
+
3
8
  ### v0.5.0
4
9
 
5
10
  * Deprecate legacy arguments in Gofer::Host.new
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Gofer!
2
2
 
3
- [![Code Climate](https://codeclimate.com/github/mipearson/gofer.png)](https://codeclimate.com/github/mipearson/gofer)
3
+ [![Code Climate](https://codeclimate.com/github/mipearson/gofer.png)](https://codeclimate.com/github/mipearson/gofer) [![Gem Version](https://badge.fury.io/rb/gofer.png)](http://badge.fury.io/rb/gofer) [![Dependency Status](https://gemnasium.com/mipearson/gofer.png)](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 = Gopher::Cluster.new
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
@@ -3,6 +3,7 @@ require 'gofer/response'
3
3
  require 'gofer/host_error'
4
4
  require 'gofer/host'
5
5
  require 'gofer/cluster'
6
+ require 'gofer/cluster_error'
6
7
  require 'gofer/version'
7
8
 
8
9
  # See Gofer::Host or Gofer::Cluster
@@ -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 = run_queue.length
91
+ length = _in.length
92
92
  _out = Queue.new
93
93
  results = {}
94
- (0...concurrency).map do
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
- results[host] = host.send(meth, *args)
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
- results
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
@@ -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
@@ -65,6 +65,11 @@ module Gofer
65
65
  channel.close # Necessary or backgrounded processes will 'hang' the channel
66
66
  end
67
67
 
68
+ if opts[:stdin]
69
+ channel.send_data(opts[:stdin])
70
+ channel.eof!
71
+ end
72
+
68
73
  end
69
74
  end
70
75
 
@@ -1,3 +1,3 @@
1
1
  module Gofer # :nodoc:
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -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
@@ -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
- prerelease:
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-06-07 00:00:00.000000000 Z
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
- version_requirements: !ruby/object:Gem::Requirement
15
+ requirement: !ruby/object:Gem::Requirement
17
16
  requirements:
18
- - - ">="
17
+ - - ! '>='
19
18
  - !ruby/object:Gem::Version
20
19
  version: 2.0.23
21
- none: false
22
- requirement: !ruby/object:Gem::Requirement
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
- version_requirements: !ruby/object:Gem::Requirement
29
+ requirement: !ruby/object:Gem::Requirement
33
30
  requirements:
34
- - - ">="
31
+ - - ! '>='
35
32
  - !ruby/object:Gem::Version
36
33
  version: 1.0.4
37
- none: false
38
- requirement: !ruby/object:Gem::Requirement
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
- none: false
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
- - ".gitignore"
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
- post_install_message:
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: !binary |-
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: !binary |-
92
- MA==
93
- none: false
89
+ version: '0'
94
90
  requirements: []
95
- rubyforge_project:
96
- rubygems_version: 1.8.24
97
- signing_key:
98
- specification_version: 3
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