ghaki-net-ssh 2011.11.30.1

Sign up to get free protection for your applications and to get access to all the features.
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