scutil 0.2.3 → 0.2.4
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/CHANGELOG +7 -0
- data/README +2 -3
- data/THANKS +1 -1
- data/lib/scutil.rb +20 -23
- data/lib/scutil/connection_cache.rb +5 -1
- data/lib/scutil/exec.rb +1 -0
- data/lib/scutil/system_connection.rb +5 -5
- data/scutil.gemspec +2 -2
- data/test/test_scutil.rb +108 -20
- metadata +2 -2
data/CHANGELOG
CHANGED
data/README
CHANGED
@@ -28,9 +28,8 @@ PTYs is beyond the scope of this documentation.
|
|
28
28
|
The "_automatic_" part of PTY requests comes from a regex in
|
29
29
|
Scutil.exec_command. Basically, if _sudo_ is at the start of the
|
30
30
|
command to be executed, scutil will request a PTY. This regex is
|
31
|
-
configurable through +:scutil_pty_regex+.
|
32
|
-
|
33
|
-
+:scutil_force_pty+ in the various _options_ arguments.
|
31
|
+
configurable through +:scutil_pty_regex+. You can force a PTY request
|
32
|
+
by specifying +:scutil_force_pty+ in the various _options_ arguments.
|
34
33
|
|
35
34
|
All of this syntactic sugar can be used as a simple class method with
|
36
35
|
Scutil.exec_command, as an instantiable class with Scutil::Exec, or as
|
data/THANKS
CHANGED
data/lib/scutil.rb
CHANGED
@@ -30,14 +30,14 @@ require 'scutil/connection_cache'
|
|
30
30
|
require 'scutil/system_connection'
|
31
31
|
|
32
32
|
module Scutil
|
33
|
-
SCUTIL_VERSION = '0.2.
|
33
|
+
SCUTIL_VERSION = '0.2.4'
|
34
34
|
# By default, buffer 10M of data before writing.
|
35
35
|
DEFAULT_OUTPUT_BUFFER_SIZE = 0xA00000
|
36
36
|
# Checks for a command starting with _sudo_ by default.
|
37
37
|
DEFAULT_PTY_REGEX = /^\s*sudo/
|
38
38
|
@connection_cache = ConnectionCache.new
|
39
39
|
@output_buffer_size = DEFAULT_OUTPUT_BUFFER_SIZE
|
40
|
-
|
40
|
+
|
41
41
|
class << self
|
42
42
|
# All successfully established connections end up here for reuse
|
43
43
|
# later.
|
@@ -45,7 +45,7 @@ module Scutil
|
|
45
45
|
# Set to 10M by default, this can be adjusted to tell scutil when
|
46
46
|
# to write command output to _output_.
|
47
47
|
attr_accessor :output_buffer_size
|
48
|
-
|
48
|
+
|
49
49
|
# Should we request a PTY? Uses custom regex if defined in
|
50
50
|
# +:scutil_pty_regex+.
|
51
51
|
#
|
@@ -86,7 +86,7 @@ module Scutil
|
|
86
86
|
# open file handle.* Finally, if _output_ is omitted, or an empty
|
87
87
|
# string, all command output will be directed to _$stdout_.
|
88
88
|
#
|
89
|
-
# <em
|
89
|
+
# <em>*NB:* This isn't actually true. The only check made is to
|
90
90
|
# see if _output_ responds to +:write+. The idea being that not
|
91
91
|
# only will a file handle have a +write+ method, but also
|
92
92
|
# something like +StringIO+. Using +StringIO+ here makes it easy
|
@@ -94,17 +94,15 @@ module Scutil
|
|
94
94
|
# better way to do this are definitely welcome.</em>
|
95
95
|
#
|
96
96
|
# Scutil will automatically request a PTY if _sudo_ is at the
|
97
|
-
# start of _cmd_.
|
98
|
-
#
|
99
|
-
#
|
100
|
-
# _options_.
|
97
|
+
# start of _cmd_. This is driven by a regex which is customizable
|
98
|
+
# via the option +:scutil_pty_regex+. You can also force a PTY
|
99
|
+
# request by specifying +:scutil_force_pty+ in _options_.
|
101
100
|
#
|
102
101
|
# Scutil.exec_command takes the following options:
|
103
102
|
#
|
104
103
|
# * :scutil_verbose => Extra output.
|
105
104
|
# * :scutil_force_pty => Force a PTY request (or not) for every channel.
|
106
|
-
# * :scutil_pty_regex => Specific a custom regex here for use when
|
107
|
-
# scutil decides whether or not to request a PTY.
|
105
|
+
# * :scutil_pty_regex => Specific a custom regex here for use when scutil decides whether or not to request a PTY.
|
108
106
|
#
|
109
107
|
# In addition, any other options passed Scutil.exec_command will
|
110
108
|
# be passed on to Net::SSH, _except_ those prefixed with
|
@@ -126,17 +124,19 @@ module Scutil
|
|
126
124
|
begin
|
127
125
|
if (Scutil.connection_cache.exists?(hostname))
|
128
126
|
sys_conn = Scutil.connection_cache.fetch(hostname)
|
129
|
-
|
127
|
+
print "[#{hostname}] Using existing connection\n" if options[:scutil_verbose]
|
130
128
|
conn = sys_conn.get_connection(hostname, username, pty_needed, options)
|
131
129
|
else
|
132
130
|
sys_conn = SystemConnection.new(hostname)
|
133
131
|
# Call get_connection first. Don't add to cache unless established.
|
134
132
|
conn = sys_conn.get_connection(hostname, username, pty_needed, options)
|
135
|
-
|
133
|
+
print "[#{hostname}] Adding new connection to cache\n" if options[:scutil_verbose]
|
136
134
|
Scutil.connection_cache << sys_conn
|
137
135
|
end
|
138
136
|
rescue Net::SSH::AuthenticationFailed => err
|
139
137
|
raise Scutil::Error.new("Error: Authenication failed for user: #{username}", hostname)
|
138
|
+
rescue SocketError => err
|
139
|
+
raise Scutil::Error.new("Error: " + err.message, hostname)
|
140
140
|
end
|
141
141
|
|
142
142
|
fh = $stdout
|
@@ -156,9 +156,9 @@ module Scutil
|
|
156
156
|
edata = ""
|
157
157
|
exit_status = 0
|
158
158
|
chan = conn.open_channel do |channel|
|
159
|
-
|
159
|
+
print "[#{conn.host}:#{channel.local_id}] Setting up callbacks...\n" if options[:scutil_verbose]
|
160
160
|
if (pty_needed)
|
161
|
-
|
161
|
+
print "[#{conn.host}:#{channel.local_id}] Requesting PTY...\n" if options[:scutil_verbose]
|
162
162
|
# OPOST is necessary, CS8 makes sense. Revisit after broader testing.
|
163
163
|
channel.request_pty(:modes => { Net::SSH::Connection::Term::CS8 => 1, Net::SSH::Connection::Term::OPOST => 0 } ) do |ch, success|
|
164
164
|
raise Scutil::Error.new("Failed to get a PTY", hostname) if !success
|
@@ -166,9 +166,9 @@ module Scutil
|
|
166
166
|
end
|
167
167
|
|
168
168
|
channel.on_data do |ch, data|
|
169
|
-
#
|
169
|
+
# print "on_data: #{data.size}\n" if options[:scutil_verbose]
|
170
170
|
odata += data
|
171
|
-
|
171
|
+
|
172
172
|
# Only buffer some of the output before writing to disk (10M by default).
|
173
173
|
if (odata.size >= Scutil.output_buffer_size)
|
174
174
|
fh.write odata
|
@@ -177,26 +177,23 @@ module Scutil
|
|
177
177
|
end
|
178
178
|
|
179
179
|
channel.on_extended_data do |ch, type, data|
|
180
|
-
#
|
180
|
+
# print "on_extended_data: #{data.size}\n" if options[:scutil_verbose]
|
181
181
|
edata += data
|
182
182
|
end
|
183
183
|
|
184
184
|
channel.on_close do |ch|
|
185
|
-
|
185
|
+
print "[#{conn.host}:#{channel.local_id}] on_close\n" if options[:scutil_verbose]
|
186
186
|
end
|
187
187
|
|
188
188
|
channel.on_open_failed do |ch, code, desc|
|
189
189
|
raise Scutil::Error.new("Failed to open channel: #{desc}", hostname, code) if !success
|
190
190
|
end
|
191
|
-
|
191
|
+
|
192
192
|
channel.on_request("exit-status") do |ch, data|
|
193
193
|
exit_status = data.read_long
|
194
|
+
print "[#{conn.host}:#{channel.local_id}] on_request(\"exit-status\"): #{exit_status}\n" if options[:scutil_verbose]
|
194
195
|
end
|
195
196
|
|
196
|
-
# channel.on_open_failed do |ch, code, desc|
|
197
|
-
#
|
198
|
-
# end
|
199
|
-
|
200
197
|
channel.exec(cmd)
|
201
198
|
# channel.wait
|
202
199
|
end
|
data/lib/scutil/exec.rb
CHANGED
@@ -25,23 +25,23 @@ module Scutil
|
|
25
25
|
if (pty_needed)
|
26
26
|
if !@pty_connection.nil?
|
27
27
|
# Existing PTY connection
|
28
|
-
|
28
|
+
print "[#{hostname}] Using existing connection (pty)\n" if @options[:scutil_verbose]
|
29
29
|
return @pty_connection
|
30
30
|
end
|
31
31
|
|
32
32
|
# New PTY connection
|
33
|
-
|
33
|
+
print "[#{hostname}] Opening new channel (pty) to system...\n" if @options[:scutil_verbose]
|
34
34
|
conn = Net::SSH.start(hostname, username, @options)
|
35
35
|
@pty_connection = conn
|
36
36
|
else
|
37
37
|
if !@connection.nil?
|
38
38
|
# Existing non-PTY connection
|
39
|
-
|
39
|
+
print "[#{hostname}] Using existing connection (non-pty)\n" if @options[:scutil_verbose]
|
40
40
|
return @connection
|
41
41
|
end
|
42
42
|
|
43
43
|
# New non-PTY connection
|
44
|
-
|
44
|
+
print "[#{hostname}] Opening channel (non-pty) to system...\n" if @options[:scutil_verbose]
|
45
45
|
conn = Net::SSH.start(hostname, username, @options)
|
46
46
|
@connection = conn
|
47
47
|
end
|
@@ -57,7 +57,7 @@ module Scutil
|
|
57
57
|
end
|
58
58
|
|
59
59
|
def to_s
|
60
|
-
"#{self.class}: #{@
|
60
|
+
"#{self.class}: #{@hostname}, @connection = #{@connection}, @pty_connection = #{@pty_connection}"
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
data/scutil.gemspec
CHANGED
data/test/test_scutil.rb
CHANGED
@@ -1,50 +1,138 @@
|
|
1
|
-
#!/usr/bin/ruby -I../lib -w
|
1
|
+
#!/usr/bin/ruby -I../lib -I. -w
|
2
2
|
|
3
3
|
require 'test/unit'
|
4
4
|
require 'scutil'
|
5
5
|
require 'stringio'
|
6
6
|
|
7
7
|
class TestScutil < Test::Unit::TestCase
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
8
|
+
TRUE_COMMAND = '/bin/true'
|
9
|
+
FALSE_COMMAND = '/bin/false'
|
10
|
+
FAKE_COMMAND = '/bin/no_such_command'
|
11
|
+
|
12
|
+
@hostname = nil
|
13
|
+
@port = nil
|
14
|
+
@user = nil
|
15
|
+
|
16
|
+
class << self
|
17
|
+
attr_accessor :hostname,:port,:user
|
18
18
|
end
|
19
19
|
|
20
20
|
def divert_stdout
|
21
|
-
@
|
21
|
+
@tmp_output = $stdout
|
22
22
|
$stdout = StringIO.new
|
23
23
|
end
|
24
24
|
|
25
25
|
def revert_stdout
|
26
26
|
@output = $stdout
|
27
|
-
$stdout = @
|
27
|
+
$stdout = @tmp_output
|
28
|
+
end
|
29
|
+
|
30
|
+
def setup
|
31
|
+
@output = nil
|
32
|
+
@tmp_output = nil
|
33
|
+
@exec = Scutil::Exec.new(TestScutil.hostname, TestScutil.user,
|
34
|
+
{ :port => TestScutil.port,
|
35
|
+
:scutil_verbose => false })
|
36
|
+
end
|
37
|
+
|
38
|
+
def teardown
|
39
|
+
@exec = nil
|
40
|
+
Scutil.connection_cache.remove_all
|
28
41
|
end
|
29
42
|
|
30
|
-
def
|
43
|
+
def test_object_initialized
|
31
44
|
assert_not_nil @exec
|
32
45
|
end
|
33
46
|
|
34
|
-
def
|
47
|
+
def test_object_is_correct_class
|
35
48
|
assert_instance_of Scutil::Exec, @exec
|
36
49
|
end
|
37
50
|
|
38
|
-
def
|
51
|
+
def test_exec_doesnt_raise_an_exception
|
52
|
+
assert_nothing_raised do
|
53
|
+
@exec.exec_command(TRUE_COMMAND)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_run_successful_command
|
58
|
+
retval = @exec.exec_command(TRUE_COMMAND)
|
59
|
+
assert_equal 0, retval
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_run_failed_command
|
63
|
+
retval = @exec.exec_command(FALSE_COMMAND)
|
64
|
+
assert_not_equal 0, retval
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_added_to_cache
|
68
|
+
@exec.exec_command(TRUE_COMMAND)
|
69
|
+
assert(Scutil.connection_cache.exists?(TestScutil.hostname))
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_exec_command_output
|
39
73
|
divert_stdout
|
40
|
-
|
74
|
+
@exec.exec_command('echo "alpha"')
|
75
|
+
# Scutil.exec_command(TestScutil.hostname, TestScutil.user, 'echo "alpha"', nil, { :port => TestScutil.port })
|
41
76
|
revert_stdout
|
42
77
|
assert_equal "alpha", @output.string.chomp
|
43
|
-
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_clear_connection
|
81
|
+
@exec.exec_command(TRUE_COMMAND)
|
82
|
+
Scutil.clear!(TestScutil.hostname)
|
83
|
+
assert(!Scutil.connection_cache.exists?(TestScutil.hostname))
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_exception_raised
|
87
|
+
assert_raises(Scutil::Error) do
|
88
|
+
@exec.exec_command(FAKE_COMMAND)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_pty_not_requested
|
93
|
+
@exec.exec_command(TRUE_COMMAND)
|
94
|
+
conn = Scutil.connection_cache.fetch(TestScutil.hostname)
|
95
|
+
assert_not_nil(conn.connection)
|
96
|
+
assert_instance_of(Net::SSH::Connection::Session, conn.connection)
|
97
|
+
assert_nil(conn.pty_connection)
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_pty_requested
|
101
|
+
@exec.exec_command("sudo " + TRUE_COMMAND)
|
102
|
+
conn = Scutil.connection_cache.fetch(TestScutil.hostname)
|
103
|
+
assert_not_nil(conn.pty_connection)
|
104
|
+
assert_instance_of(Net::SSH::Connection::Session, conn.pty_connection)
|
105
|
+
assert_nil(conn.connection)
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_option_pty_regex
|
109
|
+
@exec.exec_command("env " + TRUE_COMMAND, nil, { :scutil_pty_regex => /^env / })
|
110
|
+
conn = Scutil.connection_cache.fetch(TestScutil.hostname)
|
111
|
+
assert_not_nil(conn.pty_connection)
|
112
|
+
assert_instance_of(Net::SSH::Connection::Session, conn.pty_connection)
|
113
|
+
assert_nil(conn.connection)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_option_verbose_set_and_local_options_take_precedence
|
117
|
+
divert_stdout
|
118
|
+
@exec.exec_command(TRUE_COMMAND, nil, { :scutil_verbose => true })
|
119
|
+
revert_stdout
|
120
|
+
assert_match(/\[#{TestScutil.hostname}\]/, @output.string)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_option_force_pty
|
124
|
+
@exec.exec_command(TRUE_COMMAND, nil, { :scutil_force_pty => true })
|
125
|
+
conn = Scutil.connection_cache.fetch(TestScutil.hostname)
|
126
|
+
assert_not_nil(conn.pty_connection)
|
127
|
+
assert_instance_of(Net::SSH::Connection::Session, conn.pty_connection)
|
128
|
+
assert_nil(conn.connection)
|
44
129
|
end
|
45
130
|
end
|
46
131
|
|
47
|
-
if ARGV[
|
48
|
-
puts "Usage: #{$0}
|
132
|
+
if ARGV[0].nil?
|
133
|
+
puts "Usage: #{$0} host[:port]"
|
49
134
|
exit(1)
|
50
135
|
end
|
136
|
+
|
137
|
+
(TestScutil.hostname, TestScutil.port) = ARGV[0].split(':')
|
138
|
+
TestScutil.user = 'mas'
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: scutil
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.2.
|
5
|
+
version: 0.2.4
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Marc Soda
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-10-02 00:00:00 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: net-ssh
|