train 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: '095b1ae4856669cbe224dffbfa09f555ce7c0d05'
4
- data.tar.gz: ee6cc55a500e4466e497e853e49963a05b1f85d2
3
+ metadata.gz: f666a5aa2bb040f3bc662ef2881d2a3778764cc9
4
+ data.tar.gz: 6e84e3102772ea858d8f4827f959e74f0d7a396f
5
5
  SHA512:
6
- metadata.gz: ea321b3f07afc791e16628f7d4f26277dc7caed537e29f8351dd8934741af77b8ee137ce35184d820c2aca2e02b335d6fdb538784577f11a06e9c7be1924ef07
7
- data.tar.gz: d3a80455ea2819ccdb0ba1e92fdd94c7525604d6dfe49345902531e44eef9c06fd08d39b7911ac9464d44da4f762cd46fe10ab377804327237e4ba7e6405f987
6
+ metadata.gz: 376156ec17342f41fb82f5a1e8888e1c19b31f8760fd0ceaed4b664f68acf54c759129d3a458c0343c19d0925c70a09f715d1c8f18171d82b506c6ab331267f9
7
+ data.tar.gz: 5bce1e60af8264a419c68898e8efca072e0f5004313a9aabb1b83a978956618e21d6d3bc9de9ee848a93b482bfe6ec751d713bda18035074814b15294d5502da
data/CHANGELOG.md CHANGED
@@ -1,7 +1,21 @@
1
1
  # Change Log
2
2
 
3
- ## [1.3.0](https://github.com/chef/train/tree/1.3.0) (2018-03-29)
4
- [Full Changelog](https://github.com/chef/train/compare/v1.2.0...1.3.0)
3
+ ## [1.4.0](https://github.com/chef/train/tree/1.4.0) (2018-04-12)
4
+ [Full Changelog](https://github.com/chef/train/compare/v1.3.0...1.4.0)
5
+
6
+ **Closed issues:**
7
+
8
+ - Train reports directories with the archive bit set as files on the windows platform [\#274](https://github.com/chef/train/issues/274)
9
+
10
+ **Merged pull requests:**
11
+
12
+ - Add CloudLinux as a detected platform [\#281](https://github.com/chef/train/pull/281) ([tarcinil](https://github.com/tarcinil))
13
+ - Move Cisco IOS connection under SSH transport [\#279](https://github.com/chef/train/pull/279) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
14
+ - Initialize FileManager using '@service' [\#278](https://github.com/chef/train/pull/278) ([marcparadise](https://github.com/marcparadise))
15
+ - small fix to make sure windows directories with the archive bit set a… [\#275](https://github.com/chef/train/pull/275) ([devoptimist](https://github.com/devoptimist))
16
+
17
+ ## [v1.3.0](https://github.com/chef/train/tree/v1.3.0) (2018-03-29)
18
+ [Full Changelog](https://github.com/chef/train/compare/v1.2.0...v1.3.0)
5
19
 
6
20
  **Implemented enhancements:**
7
21
 
@@ -13,6 +27,7 @@
13
27
 
14
28
  **Merged pull requests:**
15
29
 
30
+ - Release Train 1.3.0 [\#276](https://github.com/chef/train/pull/276) ([jquick](https://github.com/jquick))
16
31
  - Add MSI connection option for azure. [\#272](https://github.com/chef/train/pull/272) ([jquick](https://github.com/jquick))
17
32
  - Add transport for Cisco IOS [\#271](https://github.com/chef/train/pull/271) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
18
33
  - Add platform uuid information. [\#270](https://github.com/chef/train/pull/270) ([jquick](https://github.com/jquick))
@@ -39,7 +39,7 @@ module Train
39
39
  end
40
40
 
41
41
  def type
42
- if attributes.include?('Archive')
42
+ if attributes.include?('Archive') && !attributes.include?('Directory')
43
43
  return :file
44
44
  elsif attributes.include?('ReparsePoint')
45
45
  return :symlink
@@ -195,6 +195,18 @@ module Train::Platforms::Detect::Specifications
195
195
  true
196
196
  end
197
197
  }
198
+ plat.name('cloudlinux').title('CloudLinux').in_family('redhat')
199
+ .detect {
200
+ lsb = read_linux_lsb
201
+ if lsb && lsb[:id] =~ /cloudlinux/i
202
+ @platform[:release] = lsb[:release]
203
+ true
204
+ elsif (raw = unix_file_contents('/etc/redhat-release')) =~ /cloudlinux/i
205
+ @platform[:name] = redhatish_platform(raw)
206
+ @platform[:release] = redhatish_version(raw)
207
+ true
208
+ end
209
+ }
198
210
  # keep redhat at the end as a fallback for anything with a redhat-release
199
211
  plat.name('redhat').title('Red Hat Linux').in_family('redhat')
200
212
  .detect {
@@ -0,0 +1,119 @@
1
+ # encoding: utf-8
2
+
3
+ class Train::Transports::SSH
4
+ class CiscoIOSConnection < BaseConnection
5
+ class BadEnablePassword < Train::TransportError; end
6
+
7
+ def initialize(options)
8
+ super(options)
9
+
10
+ # Extract options to avoid passing them in to `Net::SSH.start` later
11
+ @host = options.delete(:host)
12
+ @user = options.delete(:user)
13
+ @port = options.delete(:port)
14
+ @enable_password = options.delete(:enable_password)
15
+
16
+ # Use all options left that are not `nil` for `Net::SSH.start` later
17
+ @ssh_options = options.reject { |_key, value| value.nil? }
18
+
19
+ @prompt = /^\S+[>#]\r\n.*$/
20
+ end
21
+
22
+ def uri
23
+ "ssh://#{@user}@#{@host}:#{@port}"
24
+ end
25
+
26
+ private
27
+
28
+ def establish_connection
29
+ logger.debug("[SSH] opening connection to #{self}")
30
+
31
+ Net::SSH.start(@host, @user, @ssh_options)
32
+ end
33
+
34
+ def session
35
+ return @session unless @session.nil?
36
+
37
+ @session = open_channel(establish_connection)
38
+
39
+ # Escalate privilege to enable mode if password is given
40
+ if @enable_password
41
+ run_command_via_connection("enable\r\n#{@enable_password}")
42
+ end
43
+
44
+ # Prevent `--MORE--` by removing terminal length limit
45
+ run_command_via_connection('terminal length 0')
46
+
47
+ @session
48
+ end
49
+
50
+ def run_command_via_connection(cmd)
51
+ # Ensure buffer is empty before sending data
52
+ @buf = ''
53
+
54
+ logger.debug("[SSH] Running `#{cmd}` on #{self}")
55
+ session.send_data(cmd + "\r\n")
56
+
57
+ logger.debug('[SSH] waiting for prompt')
58
+ until @buf =~ @prompt
59
+ raise BadEnablePassword if @buf =~ /Bad secrets/
60
+ session.connection.process(0)
61
+ end
62
+
63
+ # Save the buffer and clear it for the next command
64
+ output = @buf.dup
65
+ @buf = ''
66
+
67
+ format_result(format_output(output, cmd))
68
+ end
69
+
70
+ ERROR_MATCHERS = [
71
+ 'Bad IP address',
72
+ 'Incomplete command',
73
+ 'Invalid input detected',
74
+ 'Unrecognized host',
75
+ ].freeze
76
+
77
+ # IOS commands do not have an exit code so we must compare the command
78
+ # output with partial segments of known errors. Then, we return a
79
+ # `CommandResult` with arguments in the correct position based on the
80
+ # result.
81
+ def format_result(result)
82
+ if ERROR_MATCHERS.none? { |e| result.include?(e) }
83
+ CommandResult.new(result, '', 0)
84
+ else
85
+ CommandResult.new('', result, 1)
86
+ end
87
+ end
88
+
89
+ # The buffer (@buf) contains all data sent/received on the SSH channel so
90
+ # we need to format the data to match what we would expect from Train
91
+ def format_output(output, cmd)
92
+ leading_prompt = /(\r\n|^)\S+[>#]/
93
+ command_string = /#{Regexp.quote(cmd)}\r\n/
94
+ trailing_prompt = /\S+[>#](\r\n|$)/
95
+ trailing_line_endings = /(\r\n)+$/
96
+
97
+ output
98
+ .sub(leading_prompt, '')
99
+ .sub(command_string, '')
100
+ .gsub(trailing_prompt, '')
101
+ .gsub(trailing_line_endings, '')
102
+ end
103
+
104
+ # Create an SSH channel that writes to @buf when data is received
105
+ def open_channel(ssh)
106
+ logger.debug("[SSH] opening SSH channel to #{self}")
107
+ ssh.open_channel do |ch|
108
+ ch.on_data do |_, data|
109
+ @buf += data
110
+ end
111
+
112
+ ch.send_channel_request('shell') do |_, success|
113
+ raise 'Failed to open SSH shell' unless success
114
+ logger.debug('[SSH] shell opened')
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -37,6 +37,7 @@ module Train::Transports
37
37
  name 'ssh'
38
38
 
39
39
  require 'train/transports/ssh_connection'
40
+ require 'train/transports/cisco_ios_connection'
40
41
 
41
42
  # add options for submodules
42
43
  include_options Train::Extras::CommandWrapper
@@ -193,6 +194,19 @@ module Train::Transports
193
194
 
194
195
  @connection_options = options
195
196
  conn = Connection.new(options, &block)
197
+
198
+ # Cisco IOS requires a special implementation of `Net:SSH`. This uses the
199
+ # SSH transport to identify the platform, but then replaces SSHConnection
200
+ # with a CiscoIOSConnection in order to behave as expected for the user.
201
+ if defined?(conn.platform.cisco_ios?) && conn.platform.cisco_ios?
202
+ ios_options = {}
203
+ ios_options[:host] = @options[:host]
204
+ ios_options[:user] = @options[:user]
205
+ ios_options[:enable_password] = @options[:enable_password]
206
+ ios_options.merge!(@connection_options)
207
+ conn = CiscoIOSConnection.new(ios_options)
208
+ end
209
+
196
210
  @connection = conn unless conn.nil?
197
211
  end
198
212
 
@@ -80,7 +80,7 @@ class Train::Transports::WinRM
80
80
  retry_limit: @max_wait_until_ready / delay,
81
81
  retry_delay: delay,
82
82
  )
83
- run_command(PING_COMMAND.dup)
83
+ run_command_via_connection(PING_COMMAND.dup)
84
84
  end
85
85
 
86
86
  def uri
@@ -129,7 +129,11 @@ class Train::Transports::WinRM
129
129
  # @return [Winrm::FileManager] a file transporter
130
130
  # @api private
131
131
  def file_manager
132
- @file_manager ||= WinRM::FS::FileManager.new(session)
132
+ @file_manager ||= begin
133
+ # Ensure @service is available:
134
+ wait_until_ready
135
+ WinRM::FS::FileManager.new(@service)
136
+ end
133
137
  end
134
138
 
135
139
  # Builds a `LoginCommand` for use by Linux-based platforms.
data/lib/train/version.rb CHANGED
@@ -3,5 +3,5 @@
3
3
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
4
 
5
5
  module Train
6
- VERSION = '1.3.0'.freeze
6
+ VERSION = '1.4.0'.freeze
7
7
  end
@@ -54,6 +54,16 @@ describe 'os_detect' do
54
54
  platform[:family].must_equal('redhat')
55
55
  platform[:release].must_equal('7.4')
56
56
  end
57
+ it 'sets the correct family, name, and release on CloudLinux' do
58
+ files = {
59
+ '/etc/redhat-release' => "CloudLinux release 7.4 (Georgy Grechko)\n",
60
+ '/etc/os-release' => "NAME=\"CloudLinux\"\nVERSION=\"7.4 (Georgy Grechko)\"\nID=\"cloudlinux\"\nID_LIKE=\"rhel fedora centos\"\nVERSION_ID=\"7.4\"\nPRETTY_NAME=\"CloudLinux 7.4 (Georgy Grechko)\"\nANSI_COLOR=\"0;31\"\nCPE_NAME=\"cpe:/o:cloudlinux:cloudlinux:7.4:GA:server\"\nHOME_URL=\"https://www.cloudlinux.com//\"\nBUG_REPORT_URL=\"https://www.cloudlinux.com/support\"\n",
61
+ }
62
+ platform = scan_with_files('linux', files)
63
+ platform[:name].must_equal('cloudlinux')
64
+ platform[:family].must_equal('redhat')
65
+ platform[:release].must_equal('7.4')
66
+ end
57
67
  end
58
68
  end
59
69
 
@@ -133,6 +133,15 @@ describe 'platform' do
133
133
  it { os.unix?.must_equal(true) }
134
134
  end
135
135
 
136
+ describe 'with platform set to cloudlinux' do
137
+ let(:os) { mock_platform('cloudlinux') }
138
+ it { os.redhat?.must_equal(true) }
139
+ it { os.debian?.must_equal(false) }
140
+ it { os.suse?.must_equal(false) }
141
+ it { os.linux?.must_equal(true) }
142
+ it { os.unix?.must_equal(true) }
143
+ end
144
+
136
145
  describe 'with platform set to fedora' do
137
146
  let(:os) { mock_platform('fedora') }
138
147
  it { os.fedora?.must_equal(true) }
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+
3
+ require 'helper'
4
+ require 'train/transports/ssh'
5
+
6
+ describe 'CiscoIOSConnection' do
7
+ let(:cls) do
8
+ Train::Platforms::Detect::Specifications::OS.load
9
+ plat = Train::Platforms.name('mock').in_family('cisco_ios')
10
+ plat.add_platform_methods
11
+ plat.stubs(:cisco_ios?).returns(true)
12
+ Train::Platforms::Detect.stubs(:scan).returns(plat)
13
+ Train::Transports::SSH
14
+ end
15
+
16
+ let(:opts) do
17
+ {
18
+ host: 'fakehost',
19
+ user: 'fakeuser',
20
+ password: 'fakepassword',
21
+ }
22
+ end
23
+
24
+ let(:connection) do
25
+ cls.new(opts).connection
26
+ end
27
+
28
+ describe '#initialize' do
29
+ it 'provides a uri' do
30
+ connection.uri.must_equal 'ssh://fakeuser@fakehost:22'
31
+ end
32
+ end
33
+
34
+ describe '#format_result' do
35
+ it 'returns correctly when result is `good`' do
36
+ output = 'good'
37
+ Train::Extras::CommandResult.expects(:new).with(output, '', 0)
38
+ connection.send(:format_result, 'good')
39
+ end
40
+
41
+ it 'returns correctly when result matches /Bad IP address/' do
42
+ output = "Translating \"nope\"\r\n\r\nTranslating \"nope\"\r\n\r\n% Bad IP address or host name\r\n% Unknown command or computer name, or unable to find computer address\r\n"
43
+ Train::Extras::CommandResult.expects(:new).with('', output, 1)
44
+ connection.send(:format_result, output)
45
+ end
46
+
47
+ it 'returns correctly when result matches /Incomplete command/' do
48
+ output = "% Incomplete command.\r\n\r\n"
49
+ Train::Extras::CommandResult.expects(:new).with('', output, 1)
50
+ connection.send(:format_result, output)
51
+ end
52
+
53
+ it 'returns correctly when result matches /Invalid input detected/' do
54
+ output = " ^\r\n% Invalid input detected at '^' marker.\r\n\r\n"
55
+ Train::Extras::CommandResult.expects(:new).with('', output, 1)
56
+ connection.send(:format_result, output)
57
+ end
58
+
59
+ it 'returns correctly when result matches /Unrecognized host/' do
60
+ output = "Translating \"nope\"\r\n% Unrecognized host or address, or protocol not running.\r\n\r\n"
61
+ Train::Extras::CommandResult.expects(:new).with('', output, 1)
62
+ connection.send(:format_result, output)
63
+ end
64
+ end
65
+
66
+ describe '#format_output' do
67
+ it 'returns the correct output' do
68
+ cmd = 'show calendar'
69
+ output = "show calendar\r\n10:35:50 UTC Fri Mar 23 2018\r\n7200_ios_12#\r\n7200_ios_12#"
70
+ result = connection.send(:format_output, output, cmd)
71
+ result.must_equal '10:35:50 UTC Fri Mar 23 2018'
72
+ end
73
+
74
+ it 'returns the correct output when a pipe is used' do
75
+ cmd = 'show running-config | section line con 0'
76
+ output = "show running-config | section line con 0\r\nline con 0\r\n exec-timeout 0 0\r\n privilege level 15\r\n logging synchronous\r\n stopbits 1\r\n7200_ios_12#\r\n7200_ios_12#"
77
+ result = connection.send(:format_output, output, cmd)
78
+ result.must_equal "line con 0\r\n exec-timeout 0 0\r\n privilege level 15\r\n logging synchronous\r\n stopbits 1"
79
+ end
80
+ end
81
+ end
@@ -5,10 +5,10 @@ require 'train/transports/ssh'
5
5
 
6
6
  describe 'ssh transport' do
7
7
  let(:cls) do
8
- plat = Train::Platforms.name('mock').in_family('linux')
9
- plat.add_platform_methods
10
- Train::Platforms::Detect.stubs(:scan).returns(plat)
11
- Train::Transports::SSH
8
+ plat = Train::Platforms.name('mock').in_family('linux')
9
+ plat.add_platform_methods
10
+ Train::Platforms::Detect.stubs(:scan).returns(plat)
11
+ Train::Transports::SSH
12
12
  end
13
13
  let(:conf) {{
14
14
  host: rand.to_s,
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: train
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-29 00:00:00.000000000 Z
11
+ date: 2018-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -225,7 +225,7 @@ files:
225
225
  - lib/train/plugins/transport.rb
226
226
  - lib/train/transports/aws.rb
227
227
  - lib/train/transports/azure.rb
228
- - lib/train/transports/cisco_ios.rb
228
+ - lib/train/transports/cisco_ios_connection.rb
229
229
  - lib/train/transports/docker.rb
230
230
  - lib/train/transports/local.rb
231
231
  - lib/train/transports/mock.rb
@@ -292,7 +292,7 @@ files:
292
292
  - test/unit/train_test.rb
293
293
  - test/unit/transports/aws_test.rb
294
294
  - test/unit/transports/azure_test.rb
295
- - test/unit/transports/cisco_ios.rb
295
+ - test/unit/transports/cisco_ios_connection.rb
296
296
  - test/unit/transports/local_test.rb
297
297
  - test/unit/transports/mock_test.rb
298
298
  - test/unit/transports/ssh_test.rb
@@ -383,7 +383,7 @@ test_files:
383
383
  - test/unit/train_test.rb
384
384
  - test/unit/transports/aws_test.rb
385
385
  - test/unit/transports/azure_test.rb
386
- - test/unit/transports/cisco_ios.rb
386
+ - test/unit/transports/cisco_ios_connection.rb
387
387
  - test/unit/transports/local_test.rb
388
388
  - test/unit/transports/mock_test.rb
389
389
  - test/unit/transports/ssh_test.rb
@@ -1,140 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'train/plugins'
4
- require 'train/transports/ssh'
5
-
6
- module Train::Transports
7
- class BadEnablePassword < Train::TransportError; end
8
-
9
- class CiscoIOS < SSH
10
- name 'cisco_ios'
11
-
12
- option :host, required: true
13
- option :user, required: true
14
- option :port, default: 22, required: true
15
-
16
- option :password, required: true
17
-
18
- # Used to elevate to enable mode (similar to `sudo su` in Linux)
19
- option :enable_password
20
-
21
- def connection
22
- @connection ||= Connection.new(validate_options(@options).options)
23
- end
24
-
25
- class Connection < BaseConnection
26
- def initialize(options)
27
- super(options)
28
-
29
- # Delete options to avoid passing them in to `Net::SSH.start` later
30
- @host = @options.delete(:host)
31
- @user = @options.delete(:user)
32
- @port = @options.delete(:port)
33
- @enable_password = @options.delete(:enable_password)
34
-
35
- @prompt = /^\S+[>#]\r\n.*$/
36
- end
37
-
38
- def uri
39
- "ssh://#{@user}@#{@host}:#{@port}"
40
- end
41
-
42
- private
43
-
44
- def establish_connection
45
- logger.debug("[SSH] opening connection to #{self}")
46
-
47
- Net::SSH.start(
48
- @host,
49
- @user,
50
- @options.reject { |_key, value| value.nil? },
51
- )
52
- end
53
-
54
- def session
55
- return @session unless @session.nil?
56
-
57
- @session = open_channel(establish_connection)
58
-
59
- # Escalate privilege to enable mode if password is given
60
- if @enable_password
61
- run_command_via_connection("enable\r\n#{@enable_password}")
62
- end
63
-
64
- # Prevent `--MORE--` by removing terminal length limit
65
- run_command_via_connection('terminal length 0')
66
-
67
- @session
68
- end
69
-
70
- def run_command_via_connection(cmd)
71
- # Ensure buffer is empty before sending data
72
- @buf = ''
73
-
74
- logger.debug("[SSH] Running `#{cmd}` on #{self}")
75
- session.send_data(cmd + "\r\n")
76
-
77
- logger.debug('[SSH] waiting for prompt')
78
- until @buf =~ @prompt
79
- raise BadEnablePassword if @buf =~ /Bad secrets/
80
- session.connection.process(0)
81
- end
82
-
83
- # Save the buffer and clear it for the next command
84
- output = @buf.dup
85
- @buf = ''
86
-
87
- format_result(format_output(output, cmd))
88
- end
89
-
90
- ERROR_MATCHERS = [
91
- 'Bad IP address',
92
- 'Incomplete command',
93
- 'Invalid input detected',
94
- 'Unrecognized host',
95
- ].freeze
96
-
97
- # IOS commands do not have an exit code so we must compare the command
98
- # output with partial segments of known errors. Then, we return a
99
- # `CommandResult` with arguments in the correct position based on the
100
- # result.
101
- def format_result(result)
102
- if ERROR_MATCHERS.none? { |e| result.include?(e) }
103
- CommandResult.new(result, '', 0)
104
- else
105
- CommandResult.new('', result, 1)
106
- end
107
- end
108
-
109
- # The buffer (@buf) contains all data sent/received on the SSH channel so
110
- # we need to format the data to match what we would expect from Train
111
- def format_output(output, cmd)
112
- leading_prompt = /(\r\n|^)\S+[>#]/
113
- command_string = /#{cmd}\r\n/
114
- trailing_prompt = /\S+[>#](\r\n|$)/
115
- trailing_line_endings = /(\r\n)+$/
116
-
117
- output
118
- .sub(leading_prompt, '')
119
- .sub(command_string, '')
120
- .gsub(trailing_prompt, '')
121
- .gsub(trailing_line_endings, '')
122
- end
123
-
124
- # Create an SSH channel that writes to @buf when data is received
125
- def open_channel(ssh)
126
- logger.debug("[SSH] opening SSH channel to #{self}")
127
- ssh.open_channel do |ch|
128
- ch.on_data do |_, data|
129
- @buf += data
130
- end
131
-
132
- ch.send_channel_request('shell') do |_, success|
133
- raise 'Failed to open SSH shell' unless success
134
- logger.debug('[SSH] shell opened')
135
- end
136
- end
137
- end
138
- end
139
- end
140
- end
@@ -1,94 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'helper'
4
- require 'train/transports/cisco_ios'
5
-
6
- describe 'Train::Transports::CiscoIOS' do
7
- let(:cls) do
8
- plat = Train::Platforms.name('mock').in_family('cisco_ios')
9
- plat.add_platform_methods
10
- Train::Platforms::Detect.stubs(:scan).returns(plat)
11
- Train::Transports::CiscoIOS
12
- end
13
-
14
- let(:opts) do
15
- {
16
- host: 'fakehost',
17
- user: 'fakeuser',
18
- password: 'fakepassword',
19
- }
20
- end
21
-
22
- let(:cisco_ios) do
23
- cls.new(opts)
24
- end
25
-
26
- describe 'CiscoIOS::Connection' do
27
- let(:connection) { cls.new(opts).connection }
28
-
29
- describe '#initialize' do
30
- it 'raises an error when user is missing' do
31
- opts.delete(:user)
32
- err = proc { cls.new(opts).connection }.must_raise(Train::ClientError)
33
- err.message.must_match(/must provide.*user/)
34
- end
35
-
36
- it 'raises an error when host is missing' do
37
- opts.delete(:host)
38
- err = proc { cls.new(opts).connection }.must_raise(Train::ClientError)
39
- err.message.must_match(/must provide.*host/)
40
- end
41
-
42
- it 'raises an error when password is missing' do
43
- opts.delete(:password)
44
- err = proc { cls.new(opts).connection }.must_raise(Train::ClientError)
45
- err.message.must_match(/must provide.*password/)
46
- end
47
-
48
- it 'provides a uri' do
49
- connection.uri.must_equal 'ssh://fakeuser@fakehost:22'
50
- end
51
- end
52
-
53
- describe '#format_result' do
54
- it 'returns correctly when result is `good`' do
55
- output = 'good'
56
- Train::Extras::CommandResult.expects(:new).with(output, '', 0)
57
- connection.send(:format_result, 'good')
58
- end
59
-
60
- it 'returns correctly when result matches /Bad IP address/' do
61
- output = "Translating \"nope\"\r\n\r\nTranslating \"nope\"\r\n\r\n% Bad IP address or host name\r\n% Unknown command or computer name, or unable to find computer address\r\n"
62
- Train::Extras::CommandResult.expects(:new).with('', output, 1)
63
- connection.send(:format_result, output)
64
- end
65
-
66
- it 'returns correctly when result matches /Incomplete command/' do
67
- output = "% Incomplete command.\r\n\r\n"
68
- Train::Extras::CommandResult.expects(:new).with('', output, 1)
69
- connection.send(:format_result, output)
70
- end
71
-
72
- it 'returns correctly when result matches /Invalid input detected/' do
73
- output = " ^\r\n% Invalid input detected at '^' marker.\r\n\r\n"
74
- Train::Extras::CommandResult.expects(:new).with('', output, 1)
75
- connection.send(:format_result, output)
76
- end
77
-
78
- it 'returns correctly when result matches /Unrecognized host/' do
79
- output = "Translating \"nope\"\r\n% Unrecognized host or address, or protocol not running.\r\n\r\n"
80
- Train::Extras::CommandResult.expects(:new).with('', output, 1)
81
- connection.send(:format_result, output)
82
- end
83
- end
84
-
85
- describe '#format_output' do
86
- it 'returns output containing only the output of the command executed' do
87
- cmd = 'show calendar'
88
- output = "show calendar\r\n10:35:50 UTC Fri Mar 23 2018\r\n7200_ios_12#\r\n7200_ios_12#"
89
- result = connection.send(:format_output, output, cmd)
90
- result.must_equal '10:35:50 UTC Fri Mar 23 2018'
91
- end
92
- end
93
- end
94
- end