scutil 0.2.2 → 0.2.3

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 CHANGED
@@ -1,5 +1,11 @@
1
1
 
2
- ===Changelog
2
+ ==Changelog
3
+
4
+ ===0.2.3 | 2011-09-27
5
+
6
+ * Fixed bug in Scutil.clear!
7
+ * Added beginnings of a test suite.
8
+ * Restructured code into a more sane file structure.
3
9
 
4
10
  ===0.2.2 | 2011-09-25
5
11
 
data/lib/scutil.rb CHANGED
@@ -24,53 +24,13 @@ THE SOFTWARE.
24
24
  =end
25
25
 
26
26
  require 'net/ssh'
27
+ require 'scutil/exec'
28
+ require 'scutil/error'
29
+ require 'scutil/connection_cache'
30
+ require 'scutil/system_connection'
27
31
 
28
32
  module Scutil
29
- # Utiliy class to hold all the connections created, possibly for
30
- # reuse later.
31
- class ConnectionCache
32
- attr_reader :cache
33
- include Enumerable
34
-
35
- def initialize
36
- @cache = []
37
- end
38
-
39
- # Need each to mixin Enumerable
40
- def each
41
- @cache.each do |c|
42
- yield c
43
- end
44
- end
45
-
46
- def fetch(hostname)
47
- each do |c|
48
- return c if c.hostname == hostname
49
- end
50
- end
51
-
52
- def exists?(hostname)
53
- each do |c|
54
- return true if c.hostname == hostname
55
- end
56
- false
57
- end
58
-
59
- # Remove all instances of _hostname_.
60
- def remove(hostname)
61
- @cache.delete_if { |c| c.hostname == hostname }
62
- end
63
-
64
- def <<(conn)
65
- @cache << conn
66
- end
67
-
68
- def to_s
69
- @cache.join("\n")
70
- end
71
- end
72
-
73
- SCUTIL_VERSION = '0.2.2'
33
+ SCUTIL_VERSION = '0.2.3'
74
34
  # By default, buffer 10M of data before writing.
75
35
  DEFAULT_OUTPUT_BUFFER_SIZE = 0xA00000
76
36
  # Checks for a command starting with _sudo_ by default.
@@ -85,105 +45,6 @@ module Scutil
85
45
  # Set to 10M by default, this can be adjusted to tell scutil when
86
46
  # to write command output to _output_.
87
47
  attr_accessor :output_buffer_size
88
- end
89
-
90
- # Wrapper for each connection to a system. Capabile of holding a
91
- # standard connect (@connection) and and PTY connection
92
- # (@pty_connection) for each system.
93
- class SystemConnection
94
- attr_reader :hostname,:pty_connection,:connection
95
- def initialize(hostname, options={})
96
- @hostname = hostname
97
- @connection = nil
98
- @pty_connection = nil
99
- @options = options
100
- end
101
-
102
- # Return a connection for system. Checks to see if an established
103
- # connection exists. If not it creates a new one. Requests a PTY
104
- # if needed.
105
- def get_connection(hostname, username, pty_needed=false, options={})
106
- conn = nil
107
- # Local map has precedence.
108
- @options.merge!(options)
109
-
110
- scrub_options @options
111
-
112
- if (pty_needed)
113
- if !@pty_connection.nil?
114
- # Existing PTY connection
115
- $stderr.print "[#{hostname}] Using existing connection (pty)\n" if @options[:scutil_verbose]
116
- return @pty_connection
117
- end
118
-
119
- # New PTY connection
120
- $stderr.print "[#{hostname}] Opening new channel (pty) to system...\n" if @options[:scutil_verbose]
121
- conn = Net::SSH.start(hostname, username, @options)
122
- @pty_connection = conn
123
- else
124
- if !@connection.nil?
125
- # Existing non-PTY connection
126
- $stderr.print "[#{hostname}] Using existing connection (non-pty)\n" if @options[:scutil_verbose]
127
- return @connection
128
- end
129
-
130
- # New non-PTY connection
131
- $stderr.print "[#{hostname}] Opening channel (non-pty) to system...\n" if @options[:scutil_verbose]
132
- conn = Net::SSH.start(hostname, username, @options)
133
- @connection = conn
134
- end
135
-
136
- return conn
137
- end
138
-
139
- # Remove scutil specific options. The rest go to Net::SSH.
140
- def scrub_options(options)
141
- options.delete(:scutil_verbose) if (options.has_key?(:scutil_verbose))
142
- options.delete(:scutil_force_pty) if (options.has_key?(:scutil_force_pty))
143
- options.delete(:scutil_pty_regex) if (options.has_key?(:scutil_pty_regex))
144
- end
145
-
146
- def to_s
147
- "#{self.class}: #{@name}, @connection = #{@connection}, @pty_connection = #{@pty_connection}"
148
- end
149
- end
150
-
151
- # Instantiate this class if you wish to use scutil as an object.
152
- # For example:
153
- #
154
- # exec = Scutil::Exec.new('severname', 'mas')
155
- #
156
- # exec.exec_command('echo "foo"')
157
- #
158
- # exec.exec_command('echo "bar"; sudo whoami', "",
159
- # { :scutil_force_pty => true,
160
- # :scutil_verbose => true
161
- # })
162
- #
163
- class Exec
164
- include Scutil
165
- attr_reader :hostname,:username
166
-
167
- def initialize(hostname, username, options={})
168
- @hostname = hostname
169
- @username = username
170
- @options = options
171
- end
172
-
173
- # See Scutil.exec_command. Takes _cmd_ and optionally _output_,
174
- # and _options_. Other arguments specified at class
175
- # initialization.
176
- #
177
- # The _options_ specified here will take precedence over those
178
- # specified in the constructor.
179
- def exec_command(cmd, output=nil, options={})
180
- # Local map has precedence.
181
- @options.merge!(options)
182
- Scutil.exec_command(@hostname, @username, cmd, output, @options)
183
- end
184
- end
185
-
186
- class << self
187
48
 
188
49
  # Should we request a PTY? Uses custom regex if defined in
189
50
  # +:scutil_pty_regex+.
@@ -205,16 +66,16 @@ module Scutil
205
66
  return options[:scutil_force_pty] ? true : false
206
67
  end
207
68
  end
208
-
69
+
209
70
  # Drops all instances of +hostname+ from connection_cache.
210
71
  def clear!(hostname)
211
72
  if (Scutil.connection_cache.exists?(hostname))
212
- Scutil.connection_cache.clear(hostname)
73
+ Scutil.connection_cache.remove(hostname)
213
74
  else
214
75
  raise Scutil::Error.new("Error: :scutil_pty_regex must be a kind of Regexp", hostname)
215
76
  end
216
77
  end
217
-
78
+
218
79
  # Scutil.exec_command is used to execute a command, specified in
219
80
  # _cmd_, on a remote system. The return value and any ouput of
220
81
  # the command are captured.
@@ -265,12 +126,13 @@ module Scutil
265
126
  begin
266
127
  if (Scutil.connection_cache.exists?(hostname))
267
128
  sys_conn = Scutil.connection_cache.fetch(hostname)
129
+ $stderr.print "[#{hostname}] Using existing connection\n" if options[:scutil_verbose]
268
130
  conn = sys_conn.get_connection(hostname, username, pty_needed, options)
269
131
  else
270
132
  sys_conn = SystemConnection.new(hostname)
271
- $stderr.print "[#{hostname}] Adding new connection to cache\n" if options[:scutil_verbose]
272
133
  # Call get_connection first. Don't add to cache unless established.
273
134
  conn = sys_conn.get_connection(hostname, username, pty_needed, options)
135
+ $stderr.print "[#{hostname}] Adding new connection to cache\n" if options[:scutil_verbose]
274
136
  Scutil.connection_cache << sys_conn
275
137
  end
276
138
  rescue Net::SSH::AuthenticationFailed => err
@@ -330,7 +192,11 @@ module Scutil
330
192
  channel.on_request("exit-status") do |ch, data|
331
193
  exit_status = data.read_long
332
194
  end
333
-
195
+
196
+ # channel.on_open_failed do |ch, code, desc|
197
+ #
198
+ # end
199
+
334
200
  channel.exec(cmd)
335
201
  # channel.wait
336
202
  end
@@ -352,34 +218,3 @@ module Scutil
352
218
 
353
219
  # end
354
220
  end
355
-
356
- # Exception class for scutil. The system, error message, and return
357
- # value of the remote command are stored here on error.
358
- #
359
- # begin
360
- # Scutil.exec_command('ls -al /root')
361
- # rescue Scutil::Error => err
362
- # puts "Message: " + err.message
363
- # puts "Hostname: " + err.hostname
364
- # puts "Exit status: #{err.command_exit_status}"
365
- # end
366
- #
367
- # Will produce:
368
- #
369
- # Message: Error: ls: /root: Permission denied
370
- # Hostname: server.name.com
371
- # Exit status: 2
372
- #
373
- class Scutil::Error < StandardError
374
- attr_reader :hostname,:message,:command_exit_status
375
-
376
- def initialize(message=nil, hostname=nil, command_exit_status=-1)
377
- @message = message
378
- @hostname = hostname
379
- @command_exit_status = command_exit_status
380
- end
381
-
382
- def to_s
383
- "Message: #{@message}\nHostname: #{@hostname}\nExit status: #{command_exit_status}\n"
384
- end
385
- end
@@ -0,0 +1,46 @@
1
+
2
+ module Scutil
3
+ # Utiliy class to hold all the connections created, possibly for
4
+ # reuse later.
5
+ class ConnectionCache
6
+ attr_reader :cache
7
+ include Enumerable
8
+
9
+ def initialize
10
+ @cache = []
11
+ end
12
+
13
+ # Need each to mixin Enumerable
14
+ def each
15
+ @cache.each do |c|
16
+ yield c
17
+ end
18
+ end
19
+
20
+ def fetch(hostname)
21
+ each do |c|
22
+ return c if c.hostname == hostname
23
+ end
24
+ end
25
+
26
+ def exists?(hostname)
27
+ each do |c|
28
+ return true if c.hostname == hostname
29
+ end
30
+ false
31
+ end
32
+
33
+ # Remove all instances of _hostname_.
34
+ def remove(hostname)
35
+ @cache.delete_if { |c| c.hostname == hostname }
36
+ end
37
+
38
+ def <<(conn)
39
+ @cache << conn
40
+ end
41
+
42
+ def to_s
43
+ @cache.join("\n")
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,31 @@
1
+
2
+ # Exception class for scutil. The system, error message, and return
3
+ # value of the remote command are stored here on error.
4
+ #
5
+ # begin
6
+ # Scutil.exec_command('ls -al /root')
7
+ # rescue Scutil::Error => err
8
+ # puts "Message: " + err.message
9
+ # puts "Hostname: " + err.hostname
10
+ # puts "Exit status: #{err.command_exit_status}"
11
+ # end
12
+ #
13
+ # Will produce:
14
+ #
15
+ # Message: Error: ls: /root: Permission denied
16
+ # Hostname: server.name.com
17
+ # Exit status: 2
18
+ #
19
+ class Scutil::Error < StandardError
20
+ attr_reader :hostname,:message,:command_exit_status
21
+
22
+ def initialize(message=nil, hostname=nil, command_exit_status=-1)
23
+ @message = message
24
+ @hostname = hostname
25
+ @command_exit_status = command_exit_status
26
+ end
27
+
28
+ def to_s
29
+ "Message: #{@message}\nHostname: #{@hostname}\nExit status: #{command_exit_status}\n"
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+
2
+ module Scutil
3
+ # Instantiate this class if you wish to use scutil as an object.
4
+ # For example:
5
+ #
6
+ # exec = Scutil::Exec.new('severname', 'mas')
7
+ #
8
+ # exec.exec_command('echo "foo"')
9
+ #
10
+ # exec.exec_command('echo "bar"; sudo whoami', "",
11
+ # { :scutil_force_pty => true,
12
+ # :scutil_verbose => true
13
+ # })
14
+ #
15
+ class Exec
16
+ include Scutil
17
+ attr_reader :hostname,:username
18
+
19
+ def initialize(hostname, username, options={})
20
+ @hostname = hostname
21
+ @username = username
22
+ @options = options
23
+ end
24
+
25
+ # See Scutil.exec_command. Takes _cmd_ and optionally _output_,
26
+ # and _options_. Other arguments specified at class
27
+ # initialization.
28
+ #
29
+ # The _options_ specified here will take precedence over those
30
+ # specified in the constructor.
31
+ def exec_command(cmd, output=nil, options={})
32
+ # Local map has precedence.
33
+ @options.merge!(options)
34
+ Scutil.exec_command(@hostname, @username, cmd, output, @options)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,63 @@
1
+
2
+ module Scutil
3
+ # Wrapper for each connection to a system. Capabile of holding a
4
+ # standard connect (@connection) and and PTY connection
5
+ # (@pty_connection) for each system.
6
+ class SystemConnection
7
+ attr_reader :hostname,:pty_connection,:connection
8
+ def initialize(hostname, options={})
9
+ @hostname = hostname
10
+ @connection = nil
11
+ @pty_connection = nil
12
+ @options = options
13
+ end
14
+
15
+ # Return a connection for system. Checks to see if an established
16
+ # connection exists. If not it creates a new one. Requests a PTY
17
+ # if needed.
18
+ def get_connection(hostname, username, pty_needed=false, options={})
19
+ conn = nil
20
+ # Local map has precedence.
21
+ @options.merge!(options)
22
+
23
+ scrub_options @options
24
+
25
+ if (pty_needed)
26
+ if !@pty_connection.nil?
27
+ # Existing PTY connection
28
+ $stderr.print "[#{hostname}] Using existing connection (pty)\n" if @options[:scutil_verbose]
29
+ return @pty_connection
30
+ end
31
+
32
+ # New PTY connection
33
+ $stderr.print "[#{hostname}] Opening new channel (pty) to system...\n" if @options[:scutil_verbose]
34
+ conn = Net::SSH.start(hostname, username, @options)
35
+ @pty_connection = conn
36
+ else
37
+ if !@connection.nil?
38
+ # Existing non-PTY connection
39
+ $stderr.print "[#{hostname}] Using existing connection (non-pty)\n" if @options[:scutil_verbose]
40
+ return @connection
41
+ end
42
+
43
+ # New non-PTY connection
44
+ $stderr.print "[#{hostname}] Opening channel (non-pty) to system...\n" if @options[:scutil_verbose]
45
+ conn = Net::SSH.start(hostname, username, @options)
46
+ @connection = conn
47
+ end
48
+
49
+ return conn
50
+ end
51
+
52
+ # Remove scutil specific options. The rest go to Net::SSH.
53
+ def scrub_options(options)
54
+ options.delete(:scutil_verbose) if (options.has_key?(:scutil_verbose))
55
+ options.delete(:scutil_force_pty) if (options.has_key?(:scutil_force_pty))
56
+ options.delete(:scutil_pty_regex) if (options.has_key?(:scutil_pty_regex))
57
+ end
58
+
59
+ def to_s
60
+ "#{self.class}: #{@name}, @connection = #{@connection}, @pty_connection = #{@pty_connection}"
61
+ end
62
+ end
63
+ end
data/scutil.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'scutil'
3
+ s.version = '0.2.3'
4
+ s.date = '2011-09-27'
5
+ s.summary = 'SSH Command UTILity'
6
+ s.description = <<-EOF
7
+ Scutil is a library for conveniently executing commands
8
+ remotely via SSH.
9
+ EOF
10
+ s.author = 'Marc Soda'
11
+ s.email = 'marcantoniosr@gmail.com'
12
+ s.license = 'MIT'
13
+ s.homepage = 'http://marcantonio.github.com/scutil'
14
+ s.rdoc_options << '--title' << 'SSH Command UTILity' << '--main' << 'README'
15
+ s.extra_rdoc_files = ['README', 'THANKS', 'CHANGELOG']
16
+ s.add_runtime_dependency 'net-ssh', '>= 2.1.0'
17
+ s.files = %w(
18
+ lib/scutil.rb
19
+ lib/scutil/connection_cache.rb
20
+ lib/scutil/error.rb
21
+ lib/scutil/exec.rb
22
+ lib/scutil/system_connection.rb
23
+ scutil.gemspec
24
+ README
25
+ CHANGELOG
26
+ THANKS
27
+ test/test_scutil.rb
28
+ )
29
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/ruby -I../lib -w
2
+
3
+ require 'test/unit'
4
+ require 'scutil'
5
+ require 'stringio'
6
+
7
+ class TestScutil < Test::Unit::TestCase
8
+ def setup
9
+ @hostname = ARGV[0]
10
+ @port = ARGV[1]
11
+ @user = 'mas'
12
+ @exec = Scutil::Exec.new(@hostname, @user,
13
+ { :port => @port,
14
+ # :keys => '~mas/.ssh/id_rsa',
15
+ })
16
+ @output = nil
17
+ @t_output = nil
18
+ end
19
+
20
+ def divert_stdout
21
+ @t_output = $stdout
22
+ $stdout = StringIO.new
23
+ end
24
+
25
+ def revert_stdout
26
+ @output = $stdout
27
+ $stdout = @t_output
28
+ end
29
+
30
+ def test_initialize_exec_object
31
+ assert_not_nil @exec
32
+ end
33
+
34
+ def test_is_correct_instance
35
+ assert_instance_of Scutil::Exec, @exec
36
+ end
37
+
38
+ def test_run_command
39
+ divert_stdout
40
+ retval = @exec.exec_command('echo "alpha"')
41
+ revert_stdout
42
+ assert_equal "alpha", @output.string.chomp
43
+ assert_equal 0, retval
44
+ end
45
+ end
46
+
47
+ if ARGV[1].nil?
48
+ puts "Usage: #{$0} host1 [host2 host3] port"
49
+ exit(1)
50
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: scutil
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.2.2
5
+ version: 0.2.3
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-09-25 00:00:00 Z
13
+ date: 2011-09-27 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: net-ssh
@@ -35,9 +35,15 @@ extra_rdoc_files:
35
35
  - CHANGELOG
36
36
  files:
37
37
  - lib/scutil.rb
38
+ - lib/scutil/connection_cache.rb
39
+ - lib/scutil/error.rb
40
+ - lib/scutil/exec.rb
41
+ - lib/scutil/system_connection.rb
42
+ - scutil.gemspec
38
43
  - README
39
- - THANKS
40
44
  - CHANGELOG
45
+ - THANKS
46
+ - test/test_scutil.rb
41
47
  homepage: http://marcantonio.github.com/scutil
42
48
  licenses:
43
49
  - MIT
@@ -45,6 +51,8 @@ post_install_message:
45
51
  rdoc_options:
46
52
  - --title
47
53
  - SSH Command UTILity
54
+ - --main
55
+ - README
48
56
  require_paths:
49
57
  - lib
50
58
  required_ruby_version: !ruby/object:Gem::Requirement