gofer 0.2.5 → 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/gofer/host.rb +28 -25
- data/lib/gofer/version.rb +2 -2
- data/spec/gofer/integration_spec.rb +27 -21
- metadata +29 -6
data/lib/gofer/host.rb
CHANGED
@@ -2,38 +2,41 @@ require 'tempfile'
|
|
2
2
|
|
3
3
|
module Gofer
|
4
4
|
class HostError < Exception # :nodoc:
|
5
|
-
|
5
|
+
attr_reader :host, :response
|
6
|
+
def initialize host, response, message
|
7
|
+
@host = host
|
8
|
+
@response = response
|
6
9
|
super "#{host.hostname}: #{message}"
|
7
10
|
end
|
8
11
|
end
|
9
|
-
|
12
|
+
|
10
13
|
class Host
|
11
|
-
|
14
|
+
|
12
15
|
attr_reader :hostname
|
13
16
|
attr_accessor :quiet
|
14
17
|
|
15
18
|
# Create a new Host connection
|
16
|
-
#
|
19
|
+
#
|
17
20
|
# Options:
|
18
|
-
#
|
21
|
+
#
|
19
22
|
# +quiet+:: Don't print stdout output from +run+ commands
|
20
23
|
# All other+opts+ is passed through directly to Net::SSH.start
|
21
24
|
# See http://net-ssh.github.com/ssh/v2/api/index.html for valid arguments.
|
22
25
|
def initialize _hostname, username, opts={}
|
23
26
|
@hostname = _hostname
|
24
|
-
|
27
|
+
|
25
28
|
# support legacy positional argument use
|
26
29
|
if opts.is_a? String
|
27
30
|
opts = { :keys => [opts]}
|
28
31
|
end
|
29
|
-
|
32
|
+
|
30
33
|
@quiet = opts.delete(:quiet)
|
31
|
-
|
34
|
+
|
32
35
|
# support legacy identity_file argument
|
33
36
|
if opts[:identity_file]
|
34
37
|
opts[:keys] = [opts.delete(:identity_file)]
|
35
38
|
end
|
36
|
-
|
39
|
+
|
37
40
|
@ssh = SshWrapper.new(hostname, username, opts)
|
38
41
|
end
|
39
42
|
|
@@ -41,10 +44,10 @@ module Gofer
|
|
41
44
|
#
|
42
45
|
# Raise an error if +command+ exits with a non-zero status.
|
43
46
|
#
|
44
|
-
# Print +stdout+ and +stderr+ as they're received.
|
47
|
+
# Print +stdout+ and +stderr+ as they're received.
|
45
48
|
#
|
46
49
|
# Return a Gofer::Response object.
|
47
|
-
#
|
50
|
+
#
|
48
51
|
# Options:
|
49
52
|
#
|
50
53
|
# +quiet+:: Don't print +stdout+, can also be set with +quiet=+ on the instance
|
@@ -54,19 +57,19 @@ module Gofer
|
|
54
57
|
opts[:quiet] = quiet unless opts.include?(:quiet)
|
55
58
|
response = @ssh.run command, opts
|
56
59
|
if !opts[:capture_exit_status] && response.exit_status != 0
|
57
|
-
raise HostError.new(self, "Command #{command} failed with exit status #{@ssh.last_exit_status}")
|
60
|
+
raise HostError.new(self, response, "Command #{command} failed with exit status #{@ssh.last_exit_status}")
|
58
61
|
end
|
59
62
|
response
|
60
63
|
end
|
61
|
-
|
64
|
+
|
62
65
|
# Run +commands+ one by one in order.
|
63
66
|
#
|
64
67
|
# Raise an error if a command in +commands+ exits with a non-zero status.
|
65
68
|
#
|
66
|
-
# Print +stdout+ and +stderr+ as they're received.
|
69
|
+
# Print +stdout+ and +stderr+ as they're received.
|
67
70
|
#
|
68
71
|
# Return a Gofer::Response object.
|
69
|
-
#
|
72
|
+
#
|
70
73
|
# Options:
|
71
74
|
#
|
72
75
|
# +quiet+:: Don't print +stdout+, can also be set with +quiet=+ on the instance
|
@@ -75,11 +78,11 @@ module Gofer
|
|
75
78
|
# The behaviour of passing +capture_exit_status+ here is undefined.
|
76
79
|
def run_multiple commands, opts={}
|
77
80
|
return if commands.empty?
|
78
|
-
|
81
|
+
|
79
82
|
responses = commands.map do |command|
|
80
83
|
run command, opts
|
81
84
|
end
|
82
|
-
|
85
|
+
|
83
86
|
first_response = responses.shift
|
84
87
|
responses.reduce(first_response) do |cursor, response|
|
85
88
|
Response.new(cursor.stdout + response.stdout, cursor.stderr + response.stderr, cursor.output + response.output, 0)
|
@@ -91,7 +94,7 @@ module Gofer
|
|
91
94
|
@ssh.run("sh -c '[ -e #{path} ]'").exit_status == 0
|
92
95
|
end
|
93
96
|
|
94
|
-
# Return the contents of the file at +path+.
|
97
|
+
# Return the contents of the file at +path+.
|
95
98
|
def read path
|
96
99
|
@ssh.read_file path
|
97
100
|
end
|
@@ -107,20 +110,20 @@ module Gofer
|
|
107
110
|
if response.exit_status == 0
|
108
111
|
response.stdout.strip.split("\n")
|
109
112
|
else
|
110
|
-
raise HostError.new(self, "Could not list #{path}, exit status #{response.exit_status}")
|
113
|
+
raise HostError.new(self, response, "Could not list #{path}, exit status #{response.exit_status}")
|
111
114
|
end
|
112
115
|
end
|
113
116
|
|
114
|
-
# Upload the file or directory at +from+ to +to+.
|
115
|
-
def upload from, to
|
116
|
-
@ssh.upload from, to, :recursive => File.directory?(from)
|
117
|
+
# Upload the file or directory at +from+ to +to+.
|
118
|
+
def upload from, to, opts = {}
|
119
|
+
@ssh.upload from, to, {:recursive => File.directory?(from)}.merge(opts)
|
117
120
|
end
|
118
121
|
|
119
122
|
# Download the file or directory at +from+ to +to+
|
120
|
-
def download from, to
|
121
|
-
@ssh.download from, to, :recursive => directory?(from)
|
123
|
+
def download from, to, opts = {}
|
124
|
+
@ssh.download from, to, {:recursive => directory?(from)}.merge(opts)
|
122
125
|
end
|
123
|
-
|
126
|
+
|
124
127
|
# Write +data+ to a file at +to+
|
125
128
|
def write data, to
|
126
129
|
Tempfile.open "gofer_write" do |file|
|
data/lib/gofer/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
1
|
module Gofer # :nodoc:
|
2
|
-
VERSION = "0.2.
|
3
|
-
end
|
2
|
+
VERSION = "0.2.6"
|
3
|
+
end
|
@@ -2,21 +2,21 @@ require 'spec_helper'
|
|
2
2
|
require 'tempfile'
|
3
3
|
|
4
4
|
describe Gofer do
|
5
|
-
|
5
|
+
|
6
6
|
HOSTNAME = ENV['TEST_HOST'] || 'localhost'
|
7
7
|
USERNAME = ENV['TEST_USER'] || ENV['USER']
|
8
8
|
IDENTITY_FILE = ENV['TEST_IDENTITY_FILE'] || '~/.ssh/id_rsa'
|
9
9
|
|
10
10
|
def raw_ssh command
|
11
11
|
out = `ssh -o PasswordAuthentication=no -ni #{IDENTITY_FILE} #{USERNAME}@#{HOSTNAME} #{command}`
|
12
|
-
raise "Command #{command} failed" unless $? == 0
|
12
|
+
raise "Command #{command} failed" unless $? == 0
|
13
13
|
out
|
14
14
|
end
|
15
15
|
|
16
16
|
def in_tmpdir path
|
17
17
|
File.join(@tmpdir, path)
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
def with_local_tmpdir template
|
21
21
|
f = Tempfile.new template
|
22
22
|
path = f.path
|
@@ -28,7 +28,7 @@ describe Gofer do
|
|
28
28
|
FileUtils.rm_rf path unless ENV['KEEPTMPDIR']
|
29
29
|
end
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
before :all do
|
33
33
|
@host = Gofer::Host.new(HOSTNAME, USERNAME, :keys => [IDENTITY_FILE], :quiet => true)
|
34
34
|
@tmpdir = raw_ssh("mktemp -d /tmp/gofertest.XXXXX").chomp
|
@@ -41,17 +41,17 @@ describe Gofer do
|
|
41
41
|
raw_ssh "rm -rf #{@tmpdir}" if @tmpdir && @tmpdir =~ %r{gofertest}
|
42
42
|
end
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
describe :new do
|
46
46
|
it "should support the legacy positional argument" do
|
47
47
|
Gofer::Host.new(HOSTNAME, USERNAME, IDENTITY_FILE).run("echo hello", :quiet => true).should == "hello\n"
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
it "should support the legacy identity_file key" do
|
51
51
|
Gofer::Host.new(HOSTNAME, USERNAME, :identity_file => IDENTITY_FILE).run("echo hello", :quiet => true).should == "hello\n"
|
52
52
|
end
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
describe :hostname do
|
56
56
|
it "should be the hostname of the host we're connecting to" do
|
57
57
|
@host.hostname.should == HOSTNAME
|
@@ -62,32 +62,38 @@ describe Gofer do
|
|
62
62
|
it "and capture stdout in @response.stdout" do
|
63
63
|
@response.stdout.should == "stdout\n"
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
it "and capture stderr in @response.stderr" do
|
67
67
|
@response.stderr.should == "stderr\n"
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
it "and combine captured stdout / stderr in @response.output" do
|
71
71
|
@response.output.should == "stdout\nstderr\n"
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
it "and @response by itself should be the captured stdout" do
|
75
75
|
@response.should == "stdout\n"
|
76
76
|
end
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
describe :run do
|
80
|
-
|
80
|
+
|
81
81
|
describe "with a stdout and stderr responses" do
|
82
|
-
before :all do
|
82
|
+
before :all do
|
83
83
|
@response = @host.run "echo stdout; echo stderr 1>&2", :quiet_stderr => true
|
84
84
|
end
|
85
|
-
|
85
|
+
|
86
86
|
it_should_behave_like "an output capturer"
|
87
87
|
end
|
88
88
|
|
89
89
|
it "should error if a command returns a non-zero response" do
|
90
|
-
lambda {@host.run "false"}.should raise_error
|
90
|
+
lambda {@host.run "false"}.should raise_error(/failed with exit status/)
|
91
|
+
begin
|
92
|
+
@host.run "false"
|
93
|
+
rescue Gofer::HostError => e
|
94
|
+
e.response.should be_a Gofer::Response
|
95
|
+
e.host.should be_a Gofer::Host
|
96
|
+
end
|
91
97
|
end
|
92
98
|
|
93
99
|
it "should capture a non-zero exit status if asked" do
|
@@ -95,7 +101,7 @@ describe Gofer do
|
|
95
101
|
response.exit_status.should == 1
|
96
102
|
end
|
97
103
|
end
|
98
|
-
|
104
|
+
|
99
105
|
describe :run_multiple do
|
100
106
|
describe "with stdout and stderr responses" do
|
101
107
|
before :all do
|
@@ -103,12 +109,12 @@ describe Gofer do
|
|
103
109
|
end
|
104
110
|
it_should_behave_like "an output capturer"
|
105
111
|
end
|
106
|
-
|
112
|
+
|
107
113
|
it "should error if a command returns a non-zero response" do
|
108
114
|
lambda {@host.run_multiple ["echo", "false"]}.should raise_error /failed with exit status/
|
109
115
|
end
|
110
116
|
end
|
111
|
-
|
117
|
+
|
112
118
|
describe :exist? do
|
113
119
|
it "should return true if a path or file exists" do
|
114
120
|
raw_ssh "touch #{in_tmpdir 'exists'}"
|
@@ -137,7 +143,7 @@ describe Gofer do
|
|
137
143
|
@host.read(@tmpdir + '/hello.txt').should == "hello\n"
|
138
144
|
end
|
139
145
|
end
|
140
|
-
|
146
|
+
|
141
147
|
describe :ls do
|
142
148
|
it "should list the contents of a directory" do
|
143
149
|
raw_ssh "mkdir #{@tmpdir}/lstmp && touch #{@tmpdir}/lstmp/f"
|
@@ -165,7 +171,7 @@ describe Gofer do
|
|
165
171
|
end
|
166
172
|
end
|
167
173
|
end
|
168
|
-
|
174
|
+
|
169
175
|
describe :write do
|
170
176
|
it "should write a file to the remote server" do
|
171
177
|
@host.write("some data\n", in_tmpdir('written'))
|
@@ -190,7 +196,7 @@ describe Gofer do
|
|
190
196
|
with_local_tmpdir 'download_dir' do |path|
|
191
197
|
download_dir = in_tmpdir 'download_dir'
|
192
198
|
raw_ssh "mkdir #{download_dir} && echo 'sup' > #{download_dir}/hey"
|
193
|
-
|
199
|
+
|
194
200
|
@host.download(download_dir, path)
|
195
201
|
File.open(path + '/download_dir/hey').read.should == "sup\n"
|
196
202
|
end
|
metadata
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gofer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
4
5
|
prerelease:
|
5
|
-
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
- 6
|
10
|
+
version: 0.2.6
|
6
11
|
platform: ruby
|
7
12
|
authors:
|
8
13
|
- Michael Pearson
|
@@ -10,7 +15,7 @@ autorequire:
|
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
17
|
|
13
|
-
date:
|
18
|
+
date: 2012-08-29 00:00:00 Z
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
21
|
name: net-ssh
|
@@ -20,6 +25,11 @@ dependencies:
|
|
20
25
|
requirements:
|
21
26
|
- - ">="
|
22
27
|
- !ruby/object:Gem::Version
|
28
|
+
hash: 33
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 0
|
32
|
+
- 23
|
23
33
|
version: 2.0.23
|
24
34
|
type: :runtime
|
25
35
|
version_requirements: *id001
|
@@ -31,12 +41,19 @@ dependencies:
|
|
31
41
|
requirements:
|
32
42
|
- - ">="
|
33
43
|
- !ruby/object:Gem::Version
|
44
|
+
hash: 31
|
45
|
+
segments:
|
46
|
+
- 1
|
47
|
+
- 0
|
48
|
+
- 4
|
34
49
|
version: 1.0.4
|
35
50
|
type: :runtime
|
36
51
|
version_requirements: *id002
|
37
|
-
description:
|
38
|
-
|
39
|
-
|
52
|
+
description: |
|
53
|
+
|
54
|
+
Gofer provides a flexible and reliable model for performing tasks on remote
|
55
|
+
server using Net::SSH
|
56
|
+
|
40
57
|
email:
|
41
58
|
- mipearson@gmail.com
|
42
59
|
executables: []
|
@@ -72,17 +89,23 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
72
89
|
requirements:
|
73
90
|
- - ">="
|
74
91
|
- !ruby/object:Gem::Version
|
92
|
+
hash: 3
|
93
|
+
segments:
|
94
|
+
- 0
|
75
95
|
version: "0"
|
76
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
97
|
none: false
|
78
98
|
requirements:
|
79
99
|
- - ">="
|
80
100
|
- !ruby/object:Gem::Version
|
101
|
+
hash: 3
|
102
|
+
segments:
|
103
|
+
- 0
|
81
104
|
version: "0"
|
82
105
|
requirements: []
|
83
106
|
|
84
107
|
rubyforge_project:
|
85
|
-
rubygems_version: 1.8.
|
108
|
+
rubygems_version: 1.8.17
|
86
109
|
signing_key:
|
87
110
|
specification_version: 3
|
88
111
|
summary: run commands on remote servers using SSH
|