gofer 0.2.5 → 0.2.6
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.
- 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
|