ghaki-net-ssh 2011.11.30.1

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/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Gerald Kalafut
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,23 @@
1
+ = Ghaki Net SSH - Secure Shell helpers
2
+
3
+ Ghaki Net SSH is a collection of extensions for the Net SSH gem library.
4
+
5
+ == Download
6
+
7
+ The latest version of Ghaki Net SSH can be found at
8
+
9
+ * git@github.com:ghaki/ghaki-net-ssh.git
10
+
11
+ == Installation
12
+
13
+ The preferred method of installing Ghaki Net SSH is through its GEM file.
14
+
15
+ % [sudo] gem install ghaki-net-ssh-1.0.0.gem
16
+
17
+ == License
18
+
19
+ Ghaki Net SSH is released under the MIT license.
20
+
21
+ == Support
22
+
23
+ Contact mailto:gerald@kalafut.org
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2011.11.30.1
@@ -0,0 +1,38 @@
1
+ require 'ghaki/account/base'
2
+ require 'ghaki/net_ssh/shell'
3
+
4
+ module Ghaki #:nodoc:
5
+ module NetSSH #:nodoc:
6
+
7
+ class Account < Ghaki::Account::Base
8
+ attr_accessor :logger
9
+
10
+ def initialize opts={}; super opts
11
+ @logger = opts[:logger]
12
+ end
13
+
14
+ def start_shell opts={}, &block
15
+ Ghaki::NetSSH::Shell.start \
16
+ setup_shell_opts(opts), &block
17
+ end
18
+
19
+ def start_ftp opts={}, &block
20
+ Ghaki::NetSSH::FTP.start \
21
+ setup_shell_opts(opts), &block
22
+ end
23
+
24
+ def start_telnet opts={}, &block
25
+ Ghaki::NetSSH::Telnet.start \
26
+ setup_shell_opts(opts), &block
27
+ end
28
+
29
+ protected
30
+
31
+ def setup_shell_opts opts
32
+ opts[:account] = self
33
+ opts[:logger] ||= @logger unless @logger.nil?
34
+ opts
35
+ end
36
+
37
+ end
38
+ end end
@@ -0,0 +1,3 @@
1
+ module Ghaki
2
+ class RemoteCommandError < RuntimeError; end
3
+ end
@@ -0,0 +1,72 @@
1
+ require 'delegate'
2
+ require 'forwardable'
3
+ require 'net/ssh'
4
+ require 'net/sftp'
5
+ require 'ghaki/core_ext/file/with_temp'
6
+ require 'ghaki/net_ssh/shell'
7
+
8
+ module Ghaki #:nodoc:
9
+ module NetSSH #:nodoc:
10
+
11
+ class FTP < DelegateClass(::Net::SFTP)
12
+ extend Forwardable
13
+
14
+ attr_accessor :raw_ftp, :shell
15
+ def_delegators :@shell,
16
+ :log_command_on, :log_output_on, :log_all_on,
17
+ :log_command_off, :log_output_off, :log_all_off,
18
+ :log_exec!, :log_command!
19
+
20
+ def self.start *args
21
+ ssh_gak = Ghaki::NetSSH::Shell.start( *args )
22
+ ftp_gak = Ghaki::NetSSH::FTP.new( ssh_gak )
23
+ if block_given?
24
+ begin
25
+ yield ftp_gak
26
+ ensure
27
+ ssh_gak.close
28
+ end
29
+ else
30
+ return ftp_gak
31
+ end
32
+ end
33
+
34
+ def close
35
+ @shell.close
36
+ end
37
+
38
+ def initialize ssh
39
+ @shell = ssh
40
+ @raw_ftp = @shell.raw_ssh.sftp
41
+ super(@raw_ftp)
42
+ end
43
+
44
+ def remove! fname
45
+ begin
46
+ log_command! 'SFTP', "remove #{fname}"
47
+ @raw_ftp.remove! fname
48
+ rescue ::Net::SFTP::StatusException
49
+ raise unless $!.message =~ %r{ \b no \s such \s file \b }oix
50
+ end
51
+ end
52
+
53
+ def upload! loc_file, rem_file
54
+ tmp_file = ::File.join( ::File.dirname(rem_file),
55
+ '_tmp_' + $$.to_s + '.' + ::File.basename(rem_file) )
56
+ log_command! 'SFTP', "upload #{loc_file}, #{tmp_file}"
57
+ @raw_ftp.upload! loc_file, tmp_file
58
+ log_command! 'SFTP', "rename #{tmp_file}, #{rem_file}"
59
+ @raw_ftp.rename! tmp_file, rem_file
60
+ ensure
61
+ self.remove! tmp_file
62
+ end
63
+
64
+ def download! rem_file, loc_file
65
+ log_command! 'SFTP', "download #{rem_file}, #{loc_file}"
66
+ File.with_named_temp loc_file do |tmp_file|
67
+ @raw_ftp.download! rem_file, tmp_file
68
+ end
69
+ end
70
+
71
+ end
72
+ end end
@@ -0,0 +1,136 @@
1
+ require 'ghaki/logger/mixin'
2
+
3
+ module Ghaki #:nodoc:
4
+ module NetSSH #:nodoc:
5
+
6
+ module Logger
7
+ include Ghaki::Logger::Mixin
8
+
9
+ attr_accessor :should_log_command, :should_log_output
10
+
11
+ NO_OUTPUT = '** NO OUTPUT **'
12
+
13
+ def setup_logger opts
14
+ @logger = opts[:logger]
15
+ @should_log_command = opts[:log_ssh_command]
16
+ @should_log_command = true if @should_log_command.nil?
17
+ @should_log_output = opts[:log_ssh_output]
18
+ @should_log_output = true if @should_log_output.nil?
19
+ end
20
+
21
+ ######################################################################
22
+ # TURN ON/OFF ALL LOGGING
23
+ ######################################################################
24
+
25
+ def log_all_on &block
26
+ out = ''
27
+ old_out,old_cmd = @should_log_output,@should_log_command
28
+ @should_log_output = @should_log_command = true
29
+ if not block.nil?
30
+ begin
31
+ out = block.call
32
+ ensure
33
+ @should_log_output,@should_log_command = old_out,old_cmd
34
+ end
35
+ end
36
+ out
37
+ end
38
+
39
+ def log_all_off &block
40
+ out = ''
41
+ old_out,old_cmd = @should_log_output,@should_log_command
42
+ @should_log_output = @should_log_command = false
43
+ if not block.nil?
44
+ begin
45
+ out = block.call
46
+ ensure
47
+ @should_log_output,@should_log_command = old_out,old_cmd
48
+ end
49
+ end
50
+ out
51
+ end
52
+
53
+ ######################################################################
54
+ # TURN COMMAND LOGGIN ON/OFF
55
+ ######################################################################
56
+
57
+ def log_command_on &block
58
+ out = ''
59
+ orig,@should_log_command = @should_log_command,true
60
+ if not block.nil?
61
+ begin
62
+ out = block.call
63
+ ensure
64
+ @should_log_command = orig
65
+ end
66
+ end
67
+ out
68
+ end
69
+
70
+ def log_command_off &block
71
+ out = ''
72
+ orig,@should_log_command = @should_log_command,false
73
+ if not block.nil?
74
+ begin
75
+ out = block.call
76
+ ensure
77
+ @should_log_command = orig
78
+ end
79
+ end
80
+ out
81
+ end
82
+
83
+ ######################################################################
84
+ # TURN OUTPUT LOGGIN ON/OFF
85
+ ######################################################################
86
+
87
+ def log_output_on &block
88
+ out = ''
89
+ orig,@should_log_output = @should_log_output,true
90
+ if not block.nil?
91
+ begin
92
+ out = block.call
93
+ ensure
94
+ @should_log_output = orig
95
+ end
96
+ end
97
+ out
98
+ end
99
+
100
+ def log_output_off &block
101
+ out = ''
102
+ orig,@should_log_output = @should_log_output,false
103
+ if not block.nil?
104
+ begin
105
+ out = block.call
106
+ ensure
107
+ @should_log_output = orig
108
+ end
109
+ end
110
+ out
111
+ end
112
+
113
+ ######################################################################
114
+ # DO THE ACTUAL LOGGING WORK
115
+ ######################################################################
116
+
117
+ def log_exec! title, cmd, &block
118
+ log_command! title, cmd
119
+ out = block.call || ''
120
+ return out unless @should_log_output
121
+ if out == ''
122
+ logger.puts( NO_OUTPUT )
123
+ else
124
+ logger.liner
125
+ logger.puts( out )
126
+ logger.liner
127
+ end
128
+ out
129
+ end
130
+
131
+ def log_command! title, cmd
132
+ logger.puts "#{title} #{@account.hostname} : #{cmd}" if @should_log_command
133
+ end
134
+
135
+ end
136
+ end end
@@ -0,0 +1,179 @@
1
+ require 'net/ssh'
2
+ require 'net/ssh/telnet'
3
+ require 'net/sftp'
4
+
5
+ require 'ghaki/account/base'
6
+ require 'ghaki/core_ext/file/with_temp'
7
+
8
+ require 'ghaki/net_ssh/errors'
9
+ require 'ghaki/net_ssh/ftp'
10
+ require 'ghaki/net_ssh/logger'
11
+ require 'ghaki/net_ssh/telnet'
12
+
13
+ module Ghaki #:nodoc:
14
+ module NetSSH #:nodoc:
15
+
16
+ class Shell < DelegateClass(::Net::SSH)
17
+ include Ghaki::NetSSH::Logger
18
+
19
+ DEF_TIMEOUT = 30
20
+ DEF_AUTH_METHODS = %w{
21
+ password
22
+ keyboard-interactive
23
+ publickey
24
+ hostbased
25
+ }
26
+
27
+ ######################################################################
28
+ protected
29
+ ######################################################################
30
+
31
+ def self.raw_opts_log cur_opts, raw_opts
32
+ if cur_opts.has_key?(:logger)
33
+ if cur_opts[:logger].nil?
34
+ # doesn't handle nil correctly
35
+ raw_opts.delete(:logger)
36
+ elsif cur_opts[:logger].level < ::Logger::WARN
37
+ # anything over WARN is insane
38
+ raw_opts[:logger] = cur_opts[:logger].dup
39
+ raw_opts[:logger].level = ::Logger::WARN
40
+ end
41
+ end
42
+ raw_opts
43
+ end
44
+
45
+ def self.raw_opts_setup cur_opts
46
+ raw_opts = raw_opts_log( cur_opts, cur_opts.dup )
47
+ raw_opts[:timeout] ||= DEF_TIMEOUT
48
+ raw_opts[:auth_methods] ||= DEF_AUTH_METHODS
49
+ raw_opts.delete(:log_ssh_output)
50
+ raw_opts.delete(:log_ssh_command)
51
+ raw_opts.delete(:account)
52
+ unless cur_opts[:account].password.nil?
53
+ raw_opts[:password] = cur_opts[:account].password
54
+ end
55
+ raw_opts
56
+ end
57
+
58
+ def self.args_to_opts args
59
+ case args.length
60
+ when 2
61
+ return Hash.new
62
+ when 1, 3
63
+ return args.pop
64
+ else
65
+ raise ArgumentError, "Invalid Arguments Passed: (1..3) != #{args.length}"
66
+ end
67
+ end
68
+
69
+ def self.args_to_account args, cur_opts
70
+ if args.length == 0
71
+ acc = Ghaki::Account::Base.from_opts cur_opts
72
+ else
73
+ acc = Ghaki::Account::Base.new \
74
+ :hostname => args.shift,
75
+ :username => args.shift
76
+ acc.password = Ghaki::Account::Password.parse_opts(cur_opts)
77
+ end
78
+ raise ArgumentError, 'Missing Hostname' if acc.hostname.nil?
79
+ raise ArgumentError, 'Missing Username' if acc.username.nil?
80
+ acc.collapse_opts(cur_opts)
81
+ acc
82
+ end
83
+
84
+ ######################################################################
85
+ public
86
+ ######################################################################
87
+
88
+ def self.start *args
89
+ cur_opts = args_to_opts( args )
90
+ acc = args_to_account( args, cur_opts )
91
+ raw_opts = raw_opts_setup( cur_opts )
92
+ begin
93
+ raw_ssh = ::Net::SSH.start( acc.hostname, acc.username, raw_opts )
94
+ cur_ssh = Ghaki::NetSSH::Shell.new( raw_ssh, cur_opts )
95
+ if block_given?
96
+ begin yield cur_ssh ensure raw_ssh.close end
97
+ else
98
+ return cur_ssh
99
+ end
100
+ rescue ::Net::SSH::HostKeyMismatch
101
+ $!.remember_host!
102
+ retry
103
+ rescue Net::SSH::AuthenticationFailed
104
+ if acc.retry_password?
105
+ acc.fail_password
106
+ unless cur_opts[:logger].nil?
107
+ cur_opts[:logger].warn "failed password attempt for: #{acc}"
108
+ end
109
+ retry
110
+ else
111
+ raise
112
+ end
113
+ end
114
+ end
115
+
116
+ attr_accessor :raw_ssh, :account
117
+
118
+ def initialize obj, opts={}
119
+ setup_logger opts
120
+ @account = opts[:account]
121
+ @raw_ssh = obj
122
+ super obj
123
+ end
124
+
125
+ def sftp
126
+ ftp_obj = Ghaki::NetSSH::FTP.new( self )
127
+ if block_given?
128
+ yield ftp_obj
129
+ else
130
+ return ftp_obj
131
+ end
132
+ end
133
+
134
+ def telnet opts={}
135
+ tel_obj = Ghaki::NetSSH::Telnet.new( self, opts )
136
+ if block_given?
137
+ begin
138
+ yield tel_obj
139
+ ensure
140
+ tel_obj.close
141
+ end
142
+ else
143
+ return tel_obj
144
+ end
145
+ end
146
+
147
+ def exec! cmd
148
+ self.log_exec! 'SSH', cmd do
149
+ @raw_ssh.exec!( cmd )
150
+ end
151
+ end
152
+
153
+ def remove! rem_file
154
+ sftp.remove! rem_file
155
+ end
156
+
157
+ def upload! loc_file, rem_file
158
+ sftp.upload! loc_file, rem_file
159
+ end
160
+
161
+ def download! rem_file, loc_file
162
+ sftp.download! rem_file, loc_file
163
+ end
164
+
165
+ def discover cmd, lookup
166
+ return lookup.match_lines( self.exec!(cmd).split("\n") ) do
167
+ raise RemoteCommandError, 'SSH Discovery Output Not Matched'
168
+ end
169
+ end
170
+
171
+ def redirect rem_file, loc_file, &block
172
+ sftp.remove! rem_file
173
+ out = block.call
174
+ sftp.download! rem_file, loc_file
175
+ out
176
+ end
177
+
178
+ end
179
+ end end
@@ -0,0 +1,65 @@
1
+ require 'ghaki/net_ssh/shell'
2
+ require 'ghaki/logger/spec_helper'
3
+
4
+ module Ghaki #:nodoc:
5
+ module NetSSH #:nodoc:
6
+
7
+ module SpecHelper
8
+ include Ghaki::Logger::SpecHelper
9
+
10
+ def stub_raw_net_ssh
11
+ @tel_raw = stub_everything('Net::Telnet')
12
+ ::Net::SSH::Telnet.stubs({
13
+ :new => @tel_raw,
14
+ })
15
+ @ssh_raw = stub_everything('Net::SSH')
16
+ ::Net::SSH.stubs({
17
+ :start => @ssh_raw,
18
+ })
19
+ @ftp_raw = stub_everything('Net::SFTP')
20
+ @ssh_raw.stubs({
21
+ :sftp => @ftp_raw,
22
+ })
23
+ end
24
+
25
+ def stub_gak_net_ssh
26
+ stub_raw_net_ssh
27
+ @ftp_gak = stub_everything('Ghaki::NetSSH::FTP')
28
+ Ghaki::NetSSH::FTP.stubs({
29
+ :new => @ftp_gak,
30
+ })
31
+ @tel_gak = stub_everything('Ghaki::NetSSH::Telnet')
32
+ Ghaki::NetSSH::Telnet.stubs({
33
+ :new => @tel_gak,
34
+ })
35
+ end
36
+
37
+ def clear_safe_gak_net_ssh
38
+ @ssh_gak = nil
39
+ end
40
+
41
+ def setup_safe_gak_net_ssh opts={}
42
+ return unless @ssh_gak.nil?
43
+ setup_safe_logger
44
+ stub_gak_net_ssh
45
+ opts[:hostname] ||= 'host'
46
+ opts[:username] ||= 'user'
47
+ opts[:password] ||= 'secret'
48
+ opts[:logger] ||= @logger
49
+ @ssh_gak = Ghaki::NetSSH::Shell.start(opts)
50
+ @ssh_gak.stubs({
51
+ :sftp => @ftp_gak,
52
+ :telnet => @tel_gak,
53
+ })
54
+ Ghaki::NetSSH::Shell.stubs({
55
+ :new => @ssh_gak,
56
+ })
57
+ end
58
+
59
+ def reset_safe_gak_net_ssh opts={}
60
+ clear_safe_gak_net_ssh
61
+ setup_safe_gak_net_ssh opts
62
+ end
63
+
64
+ end
65
+ end end
@@ -0,0 +1,72 @@
1
+ require 'delegate'
2
+ require 'forwardable'
3
+ require 'net/ssh'
4
+ require 'net/ssh/telnet'
5
+ require 'ghaki/net_ssh/shell'
6
+
7
+ module Ghaki #:nodoc:
8
+ module NetSSH #:nodoc:
9
+
10
+ class Telnet < DelegateClass(::Net::SSH::Telnet)
11
+ extend Forwardable
12
+
13
+ attr_accessor :raw_telnet, :shell, :auto_close
14
+
15
+ def_delegators :@shell,
16
+ :log_command_on, :log_output_on, :log_all_on,
17
+ :log_command_off, :log_output_off, :log_all_off,
18
+ :log_exec!, :log_command!
19
+
20
+ def self.args_to_tel_opts args
21
+ case args.length
22
+ when 2
23
+ return Hash.new
24
+ when 1, 3
25
+ return args.last[:telnet_options] || {}
26
+ else
27
+ raise ArgumentError, "Invalid Arguments Passed: (1..3) != #{args.length}"
28
+ end
29
+ end
30
+
31
+ def self.start *args, &block
32
+ tel_opt = args_to_tel_opts( args )
33
+ gak_ssh = Shell.start( *args )
34
+ gak_tel = Telnet.new( gak_ssh, tel_opt )
35
+ gak_tel.auto_close = true
36
+ if block_given?
37
+ begin
38
+ block.call( gak_tel )
39
+ ensure
40
+ gak_tel.close
41
+ end
42
+ else
43
+ return gak_tel
44
+ end
45
+ end
46
+
47
+ def auto_close?
48
+ @auto_close
49
+ end
50
+
51
+ def close
52
+ super
53
+ @shell.close if @auto_close
54
+ end
55
+
56
+ def initialize ssh, in_opts={}
57
+ @auto_close = false
58
+ @shell = ssh
59
+ out_opts = in_opts.dup
60
+ out_opts['Session'] = @shell.raw_ssh
61
+ @raw_telnet = ::Net::SSH::Telnet.new(out_opts)
62
+ super @raw_telnet
63
+ end
64
+
65
+ def exec! cmd
66
+ log_exec!( 'TELNET', cmd ) do
67
+ @raw_telnet.cmd( cmd )
68
+ end
69
+ end
70
+
71
+ end
72
+ end end
@@ -0,0 +1,12 @@
1
+ require 'net/ssh'
2
+ require 'net/ssh/telnet'
3
+ require 'net/sftp'
4
+
5
+ require 'ghaki/account/base'
6
+ require 'ghaki/core_ext/file/with_temp'
7
+
8
+ require 'ghaki/net_ssh/errors'
9
+ require 'ghaki/net_ssh/shell'
10
+ require 'ghaki/net_ssh/ftp'
11
+ require 'ghaki/net_ssh/logger'
12
+ require 'ghaki/net_ssh/telnet'
@@ -0,0 +1,42 @@
1
+ require 'ghaki/net_ssh/common_helper'
2
+ require 'ghaki/net_ssh/account'
3
+
4
+ module Ghaki module NetSSH module Account_Testing
5
+ describe Account do
6
+ include CommonHelper
7
+
8
+ before(:each) do
9
+ setup_common
10
+ @account = Ghaki::NetSSH::Account.new(@user_opts)
11
+ end
12
+
13
+ it { should be_kind_of(Ghaki::Account::Base) }
14
+ it { should respond_to :logger }
15
+ it { should respond_to :logger= }
16
+
17
+ describe '#start_shell' do
18
+ it 'should create shell connection' do
19
+ Ghaki::NetSSH::Shell.expects(:start).
20
+ with( :account => subject ).returns(:shell_started)
21
+ subject.start_shell.should == :shell_started
22
+ end
23
+ end
24
+
25
+ describe '#start_ftp' do
26
+ it 'should create ftp connection' do
27
+ Ghaki::NetSSH::FTP.expects(:start).
28
+ with( :account => subject ).returns(:sftp_started)
29
+ subject.start_ftp.should == :sftp_started
30
+ end
31
+ end
32
+
33
+ describe '#start_telnet' do
34
+ it 'should create telnet connection' do
35
+ Ghaki::NetSSH::Telnet.expects(:start).
36
+ with( :account => subject ).returns(:telnet_started)
37
+ subject.start_telnet.should == :telnet_started
38
+ end
39
+ end
40
+
41
+ end
42
+ end end end
@@ -0,0 +1,22 @@
1
+ require 'ghaki/account/base'
2
+ require 'ghaki/net_ssh/spec_helper'
3
+
4
+ module CommonHelper
5
+ include Ghaki::NetSSH::SpecHelper
6
+
7
+ def setup_common
8
+ setup_safe_logger
9
+ stub_raw_net_ssh
10
+ @user_opts = {
11
+ :username => 'user',
12
+ :hostname => 'host',
13
+ :password => 'secret',
14
+ }
15
+ @account = Ghaki::Account::Base.new @user_opts
16
+ @test_opts = {
17
+ :account => @account,
18
+ :logger => @logger,
19
+ }
20
+ end
21
+
22
+ end
@@ -0,0 +1,75 @@
1
+ require 'ghaki/net_ssh/ftp'
2
+ require 'ghaki/net_ssh/common_helper'
3
+
4
+ module Ghaki module NetSSH module FTP_Testing
5
+ describe FTP do
6
+ include CommonHelper
7
+
8
+ before(:each) do
9
+ setup_common
10
+ end
11
+
12
+ context 'class' do
13
+ subject { FTP }
14
+ describe '#start' do
15
+ it 'should yield ftp' do
16
+ @ssh_raw.expects(:close).once
17
+ FTP.start(@test_opts) do |sftp|
18
+ sftp.should be_an_instance_of(FTP)
19
+ end
20
+ end
21
+ it 'should return ftp' do
22
+ @ssh_raw.expects(:close).once
23
+ sftp = FTP.start(@test_opts)
24
+ sftp.should be_an_instance_of(FTP)
25
+ sftp.close
26
+ end
27
+ end
28
+ end
29
+
30
+ context 'object' do
31
+
32
+ before(:each) do
33
+ @ftp_gak = FTP.start(@test_opts)
34
+ end
35
+ subject { @ftp_gak }
36
+
37
+ context 'logging delegates' do
38
+ [ :log_command_on, :log_output_on, :log_all_on,
39
+ :log_command_off, :log_output_off, :log_all_off,
40
+ :log_exec!, :log_command!,
41
+ ].each do |token|
42
+ it { should respond_to token }
43
+ end
44
+ end
45
+
46
+ describe '#remove!' do
47
+ it 'should delegate remove' do
48
+ @ftp_raw.expects(:remove!).with('remote_file')
49
+ @ftp_gak.remove! 'remote_file'
50
+ end
51
+ end
52
+
53
+ describe '#upload!' do
54
+ it 'should delegate upload, rename, and remove' do
55
+ seq = sequence('uploader')
56
+ src = 'local_file'; dst = 'remote_file'
57
+ @ftp_raw.expects(:upload!).with(src,is_a(String)).in_sequence(seq)
58
+ @ftp_raw.expects(:rename!).with(is_a(String),dst).in_sequence(seq)
59
+ @ftp_raw.expects(:remove!).with(is_a(String)).in_sequence(seq)
60
+ @ftp_gak.upload! src, dst
61
+ end
62
+ end
63
+
64
+ describe '#download!' do
65
+ it 'should delegate download' do
66
+ src = 'remote_file'; dst = 'local_file'; tmp = 'tmp_file'
67
+ ::File.expects(:with_named_temp).yields(tmp)
68
+ @ftp_raw.expects(:download!).with(src,tmp)
69
+ @ftp_gak.download! src, dst
70
+ end
71
+ end
72
+ end
73
+
74
+ end
75
+ end end end
@@ -0,0 +1,180 @@
1
+ require 'ghaki/net_ssh/logger'
2
+ require 'ghaki/net_ssh/common_helper'
3
+
4
+ module Ghaki module NetSSH module Logger_Testing
5
+ describe Logger do
6
+ include CommonHelper
7
+
8
+ before(:each) do
9
+ setup_common
10
+ end
11
+
12
+ class UsingLogger
13
+ include Logger
14
+ attr_accessor :account
15
+ def initialize opts={}
16
+ setup_logger opts
17
+ @account = opts[:account]
18
+ end
19
+ end
20
+
21
+ context 'objects including' do
22
+
23
+ describe '#setup_logger' do
24
+ it 'should accept opt <log_ssh_command>' do
25
+ @subj = UsingLogger.new( :logger => @logger, :log_ssh_command => false )
26
+ @subj.should_log_command.should be_false
27
+ end
28
+ it 'should default opt <log_ssh_command>' do
29
+ @subj = UsingLogger.new( :logger => @logger )
30
+ @subj.should_log_command.should be_true
31
+ end
32
+ it 'should accept opt <log_ssh_output>' do
33
+ @subj = UsingLogger.new( :logger => @logger, :log_ssh_output => false )
34
+ @subj.should_log_output.should be_false
35
+ end
36
+ it 'should default opt <log_ssh_output>' do
37
+ @subj = UsingLogger.new( :logger => @logger )
38
+ @subj.should_log_output.should be_true
39
+ end
40
+ end
41
+
42
+ before(:each) do
43
+ @subj = UsingLogger.new(@test_opts)
44
+ end
45
+ subject { @subj }
46
+
47
+ describe '#log_command!' do
48
+ it 'should log title, command, and host' do
49
+ @logger.expects(:puts).with('SSH host : who').once
50
+ @subj.log_command! 'SSH', 'who'
51
+ end
52
+ it 'should not log when supressed' do
53
+ @subj.should_log_command = false
54
+ @logger.expects(:puts).never
55
+ @subj.log_command! 'SSH', 'who'
56
+ end
57
+ end
58
+
59
+ describe '#log_exec!' do
60
+ it 'should log title and output' do
61
+ @logger.expects(:puts).with('SSH host : who').once
62
+ @logger.expects(:puts).with('output').once
63
+ @logger.expects(:liner).twice
64
+ @subj.log_exec!('SSH', 'who') do 'output' end.should == 'output'
65
+ end
66
+ it 'should log only output when command is supressed' do
67
+ @subj.should_log_command = false
68
+ @logger.expects(:puts).with('SSH host : who').never
69
+ @logger.expects(:puts).with('output').once
70
+ @logger.expects(:liner).twice
71
+ @subj.log_exec!('SSH', 'who') do 'output' end.should == 'output'
72
+ end
73
+ it 'should not log when both are supressed' do
74
+ @subj.should_log_command = false
75
+ @subj.should_log_output = false
76
+ @logger.expects(:puts).never
77
+ @logger.expects(:liner).never
78
+ @subj.log_exec!('SSH', 'who') do 'output' end.should == 'output'
79
+ end
80
+ it 'should default for no output' do
81
+ @subj.should_log_command = false
82
+ @logger.expects(:puts).with('** NO OUTPUT **').once
83
+ @logger.expects(:liner).never
84
+ @subj.log_exec!('SSH', 'who') do nil end.should == ''
85
+ end
86
+ end
87
+
88
+ describe '#log_all_on' do
89
+ before(:each) do
90
+ @subj.should_log_command = @subj.should_log_output = false
91
+ end
92
+ it 'should apply only within block' do
93
+ @subj.log_all_on do
94
+ @subj.should_log_command.should be_true
95
+ @subj.should_log_output.should be_true
96
+ end
97
+ @subj.should_log_command.should be_false
98
+ @subj.should_log_output.should be_false
99
+ end
100
+ it 'should set permanently' do
101
+ @subj.log_all_on
102
+ @subj.should_log_command.should be_true
103
+ @subj.should_log_output.should be_true
104
+ end
105
+ end
106
+
107
+ describe '#log_all_off' do
108
+ it 'should apply only within block' do
109
+ @subj.log_all_off do
110
+ @subj.should_log_command.should be_false
111
+ @subj.should_log_output.should be_false
112
+ end
113
+ @subj.should_log_command.should be_true
114
+ @subj.should_log_output.should be_true
115
+ end
116
+ it 'should set permanently' do
117
+ @subj.log_all_off
118
+ @subj.should_log_command.should be_false
119
+ @subj.should_log_output.should be_false
120
+ end
121
+ end
122
+
123
+ describe '#log_command_on' do
124
+ before(:each) do @subj.should_log_command = false end
125
+ it 'should apply only within block' do
126
+ @subj.log_command_on do
127
+ @subj.should_log_command.should be_true
128
+ end
129
+ @subj.should_log_command.should be_false
130
+ end
131
+ it 'should set permanently' do
132
+ @subj.log_command_on
133
+ @subj.should_log_command.should be_true
134
+ end
135
+ end
136
+
137
+ describe '#log_command_off' do
138
+ it 'should apply only within block' do
139
+ @subj.log_command_off do
140
+ @subj.should_log_command.should be_false
141
+ end
142
+ @subj.should_log_command.should be_true
143
+ end
144
+ it 'should set permanently' do
145
+ @subj.log_command_off
146
+ @subj.should_log_command.should be_false
147
+ end
148
+ end
149
+
150
+ describe '#log_output_on' do
151
+ before(:each) do @subj.should_log_output = false end
152
+ it 'should apply only within block' do
153
+ @subj.log_output_on do
154
+ @subj.should_log_output.should be_true
155
+ end
156
+ @subj.should_log_output.should be_false
157
+ end
158
+ it 'should set permanently' do
159
+ @subj.log_output_on
160
+ @subj.should_log_output.should be_true
161
+ end
162
+ end
163
+
164
+ describe '#log_output_off' do
165
+ it 'should apply only within block' do
166
+ @subj.log_output_off do
167
+ @subj.should_log_output.should be_false
168
+ end
169
+ @subj.should_log_output.should be_true
170
+ end
171
+ it 'should set permanently' do
172
+ @subj.log_output_off
173
+ @subj.should_log_output.should be_false
174
+ end
175
+ end
176
+
177
+ end
178
+
179
+ end
180
+ end end end
@@ -0,0 +1,138 @@
1
+ require 'ghaki/net_ssh/shell'
2
+ require 'ghaki/net_ssh/common_helper'
3
+ require 'ghaki/match/parser/base'
4
+
5
+ module Ghaki module NetSSH module Shell_Testing
6
+ describe Shell do
7
+ include CommonHelper
8
+
9
+ before(:each) do
10
+ setup_common
11
+ end
12
+
13
+ context 'eigen class' do
14
+ subject { Shell }
15
+
16
+ describe '#start' do
17
+ it 'returns ssh' do
18
+ subject.start(@test_opts).should be_an_instance_of(Shell)
19
+ end
20
+ it 'yields ssh' do
21
+ @ssh_raw.expects(:close).once
22
+ subject.start(@test_opts) do |ssh|
23
+ ssh.should be_an_instance_of(Shell)
24
+ end
25
+ end
26
+ it 'handles password retries' do
27
+ @account.passwords = ['invalid','secret']
28
+ ::Net::SSH.expects(:start).raises(::Net::SSH::AuthenticationFailed).then.returns(@ssh_raw)
29
+ @logger.expects(:warn).with(regexp_matches(%r{failed\spassword\sattempt}))
30
+ subject.start(@test_opts).should be_an_instance_of(Shell)
31
+ @account.failed_passwords?.should be_true
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ context 'object instance' do
38
+ before(:each) do
39
+ @ssh_gak = Shell.start(@test_opts)
40
+ end
41
+ subject { @ssh_gak }
42
+
43
+ describe '#exec!' do
44
+ it 'delegates to ssh' do
45
+ @ssh_raw.expects(:exec!).with('who').returns('nobody')
46
+ @ssh_gak.exec! 'who'
47
+ end
48
+ end
49
+
50
+ describe '#telnet' do
51
+ it 'creates telnet' do
52
+ tel = @ssh_gak.telnet
53
+ tel.should be_an_instance_of(Telnet)
54
+ end
55
+ it 'yields telnet' do
56
+ @tel_raw.expects(:close).once
57
+ @ssh_gak.telnet do |tel|
58
+ tel.should be_an_instance_of(Telnet)
59
+ end
60
+ end
61
+ end
62
+
63
+ describe '#discover' do
64
+ before(:each) do
65
+ @matcher = Ghaki::Match::Parser::Base.new({
66
+ %r{foo}o => :foo,
67
+ })
68
+ end
69
+ it 'matches if found' do
70
+ @ssh_raw.expects(:exec!).with('who').returns('foo')
71
+ @ssh_gak.discover( 'who', @matcher ).should == :foo
72
+ end
73
+ it 'rejects if not found' do
74
+ lambda do
75
+ @ssh_raw.expects(:exec!).with('who').returns('bar')
76
+ @ssh_gak.discover( 'who', @matcher )
77
+ end.should raise_error(RemoteCommandError)
78
+ end
79
+ end
80
+
81
+ describe '#sftp' do
82
+ it 'creates ftp' do
83
+ ftp = @ssh_gak.sftp
84
+ ftp.should be_an_instance_of(FTP)
85
+ end
86
+ it 'yields ftp' do
87
+ @ssh_gak.sftp do |ftp|
88
+ ftp.should be_an_instance_of(FTP)
89
+ end
90
+ end
91
+ end
92
+
93
+ ####################################################################
94
+ describe 'sftp helpers' do
95
+
96
+ before(:each) do
97
+ @ftp_gak = mock('Ghaki::NetSSH::FTP')
98
+ FTP.stubs( :new => @ftp_gak )
99
+ end
100
+
101
+ describe '#remove!' do
102
+ it 'delegates to ftp' do
103
+ trg = 'remote_file'
104
+ @ftp_gak.expects(:remove!).with(trg)
105
+ @ssh_gak.remove! trg
106
+ end
107
+ end
108
+
109
+ describe '#upload!' do
110
+ it 'delegates to ftp' do
111
+ src,dst = 'local_file', 'remote_file'
112
+ @ftp_gak.expects(:upload!).with(src,dst)
113
+ @ssh_gak.upload! src, dst
114
+ end
115
+ end
116
+
117
+ describe '#download!' do
118
+ it 'delegates to ftp' do
119
+ src,dst = 'remote_file', 'local_file'
120
+ @ftp_gak.expects(:download!).with(src,dst)
121
+ @ssh_gak.download! src, dst
122
+ end
123
+ end
124
+
125
+ describe '#redirect' do
126
+ it 'delegates to ftp' do
127
+ src,dst = 'remote_file', 'local_file'
128
+ put = 'output'
129
+ @ftp_gak.expects(:remove!).with(src)
130
+ @ftp_gak.expects(:download!).with(src,dst)
131
+ @ssh_gak.redirect(src,dst) do put end.should == put
132
+ end
133
+ end
134
+ end
135
+
136
+ end
137
+ end
138
+ end end end
@@ -0,0 +1,67 @@
1
+ require 'ghaki/net_ssh/telnet'
2
+ require 'ghaki/net_ssh/common_helper'
3
+
4
+ module Ghaki module NetSSH module Telnet_Testing
5
+ describe Telnet do
6
+ include CommonHelper
7
+
8
+ before(:each) do
9
+ setup_common
10
+ end
11
+
12
+ ########################################################################
13
+ context 'class' do
14
+
15
+ subject { Telnet }
16
+
17
+ describe '#start' do
18
+
19
+ it 'should yield telnet' do
20
+ @tel_raw.expects(:close).once
21
+ @ssh_raw.expects(:close).once
22
+ Telnet.start(@test_opts) do |tel|
23
+ tel.should be_an_instance_of(Telnet)
24
+ end
25
+ end
26
+
27
+ it 'should return telnet' do
28
+ @tel_raw.expects(:close).once
29
+ @ssh_raw.expects(:close).once
30
+ tel = Telnet.start(@test_opts)
31
+ tel.should be_an_instance_of(Telnet)
32
+ tel.close
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+
39
+ ########################################################################
40
+ context 'object' do
41
+
42
+ before(:each) do
43
+ @tel_gak = Telnet.start(@test_opts)
44
+ end
45
+ subject { @tel_gak }
46
+
47
+ context 'logging delegates' do
48
+ [ :log_command_on, :log_output_on, :log_all_on,
49
+ :log_command_off, :log_output_off, :log_all_off,
50
+ :log_exec!, :log_command!,
51
+ ].each do |token|
52
+ it { should respond_to token }
53
+ end
54
+ end
55
+
56
+ describe '#exec!' do
57
+ it 'should delegate to telnet' do
58
+ @tel_raw.expects(:cmd).with('who').returns('moo')
59
+ @tel_gak.exec!('who').should == 'moo'
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end end end
67
+ ############################################################################
@@ -0,0 +1,7 @@
1
+ require 'bundler/setup'
2
+ require 'mocha'
3
+
4
+ RSpec.configure do |cfg|
5
+ cfg.mock_with :mocha
6
+ cfg.color_enabled = true
7
+ end
metadata ADDED
@@ -0,0 +1,202 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ghaki-net-ssh
3
+ version: !ruby/object:Gem::Version
4
+ version: 2011.11.30.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gerald Kalafut
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-11-30 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ghaki-account
16
+ requirement: &74945690 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2011.11.29.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *74945690
25
+ - !ruby/object:Gem::Dependency
26
+ name: ghaki-app
27
+ requirement: &74945450 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 2011.11.29.1
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *74945450
36
+ - !ruby/object:Gem::Dependency
37
+ name: ghaki-ext-file
38
+ requirement: &74945230 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 2011.11.29.1
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *74945230
47
+ - !ruby/object:Gem::Dependency
48
+ name: ghaki-logger
49
+ requirement: &74945000 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 2011.11.29.1
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *74945000
58
+ - !ruby/object:Gem::Dependency
59
+ name: highline
60
+ requirement: &74944780 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: 1.6.1
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *74944780
69
+ - !ruby/object:Gem::Dependency
70
+ name: net-ssh
71
+ requirement: &74944530 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: 2.2.1
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: *74944530
80
+ - !ruby/object:Gem::Dependency
81
+ name: net-ssh-telnet
82
+ requirement: &74944270 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: 0.0.2
88
+ type: :runtime
89
+ prerelease: false
90
+ version_requirements: *74944270
91
+ - !ruby/object:Gem::Dependency
92
+ name: net-sftp
93
+ requirement: &74944030 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: 2.0.5
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: *74944030
102
+ - !ruby/object:Gem::Dependency
103
+ name: rspec
104
+ requirement: &74943800 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 2.6.0
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: *74943800
113
+ - !ruby/object:Gem::Dependency
114
+ name: rdoc
115
+ requirement: &74943570 !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: 3.9.4
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: *74943570
124
+ - !ruby/object:Gem::Dependency
125
+ name: mocha
126
+ requirement: &74943320 !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: 0.9.12
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: *74943320
135
+ - !ruby/object:Gem::Dependency
136
+ name: ghaki-match
137
+ requirement: &74943090 !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: 2011.11.30.1
143
+ type: :development
144
+ prerelease: false
145
+ version_requirements: *74943090
146
+ description: Ghaki Net SSH is a collection of extensions for the Net SSH gem library.
147
+ email: gerald@kalafut.org
148
+ executables: []
149
+ extensions: []
150
+ extra_rdoc_files:
151
+ - README
152
+ files:
153
+ - lib/ghaki/net_ssh/logger.rb
154
+ - lib/ghaki/net_ssh/ftp.rb
155
+ - lib/ghaki/net_ssh/account.rb
156
+ - lib/ghaki/net_ssh/telnet.rb
157
+ - lib/ghaki/net_ssh/spec_helper.rb
158
+ - lib/ghaki/net_ssh/errors.rb
159
+ - lib/ghaki/net_ssh/shell.rb
160
+ - lib/ghaki/net_ssh.rb
161
+ - README
162
+ - LICENSE
163
+ - VERSION
164
+ - spec/ghaki/net_ssh/account_spec.rb
165
+ - spec/ghaki/net_ssh/logger_spec.rb
166
+ - spec/ghaki/net_ssh/ftp_spec.rb
167
+ - spec/ghaki/net_ssh/telnet_spec.rb
168
+ - spec/ghaki/net_ssh/shell_spec.rb
169
+ - spec/spec_helper.rb
170
+ - spec/ghaki/net_ssh/common_helper.rb
171
+ homepage: http://github.com/ghaki
172
+ licenses: []
173
+ post_install_message:
174
+ rdoc_options: []
175
+ require_paths:
176
+ - lib
177
+ required_ruby_version: !ruby/object:Gem::Requirement
178
+ none: false
179
+ requirements:
180
+ - - ! '>='
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ required_rubygems_version: !ruby/object:Gem::Requirement
184
+ none: false
185
+ requirements:
186
+ - - ! '>='
187
+ - !ruby/object:Gem::Version
188
+ version: 1.3.6
189
+ requirements: []
190
+ rubyforge_project: ghaki-net-ssh
191
+ rubygems_version: 1.8.10
192
+ signing_key:
193
+ specification_version: 3
194
+ summary: Secure Shell helpers
195
+ test_files:
196
+ - spec/ghaki/net_ssh/account_spec.rb
197
+ - spec/ghaki/net_ssh/logger_spec.rb
198
+ - spec/ghaki/net_ssh/ftp_spec.rb
199
+ - spec/ghaki/net_ssh/telnet_spec.rb
200
+ - spec/ghaki/net_ssh/shell_spec.rb
201
+ - spec/spec_helper.rb
202
+ - spec/ghaki/net_ssh/common_helper.rb