train 1.4.25 → 1.4.29
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -7
- data/lib/train/file.rb +78 -19
- data/lib/train/transports/azure.rb +3 -37
- data/lib/train/transports/helpers/azure/file_credentials.rb +42 -0
- data/lib/train/transports/helpers/azure/file_parser.rb +25 -0
- data/lib/train/transports/helpers/azure/subscription_id_file_parser.rb +24 -0
- data/lib/train/transports/helpers/azure/subscription_number_file_parser.rb +30 -0
- data/lib/train/transports/ssh.rb +2 -1
- data/lib/train/transports/ssh_connection.rb +1 -0
- data/lib/train/version.rb +1 -1
- data/test/integration/tests/path_block_device_test.rb +2 -2
- data/test/integration/tests/path_folder_test.rb +4 -4
- data/test/integration/tests/path_missing_test.rb +4 -4
- data/test/integration/tests/path_pipe_test.rb +0 -15
- data/test/unit/file/local/unix_test.rb +79 -1
- data/test/unit/file/local/windows_test.rb +60 -0
- data/test/unit/file/remote/aix_test.rb +48 -0
- data/test/unit/file/remote/linux_test.rb +49 -1
- data/test/unit/file/remote/qnx_test.rb +37 -1
- data/test/unit/file/remote/unix_test.rb +77 -2
- data/test/unit/file/remote/windows_test.rb +72 -0
- data/test/unit/file_test.rb +2 -29
- data/test/unit/transports/azure_test.rb +0 -55
- data/test/unit/transports/helpers/azure/file_credentials_test.rb +121 -0
- data/test/unit/transports/ssh_test.rb +12 -0
- data/test/windows/winrm_test.rb +16 -6
- data/train.gemspec +3 -3
- metadata +13 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65be3294011740eca689d8b5de1273f14f37d5189eae2a29111a4d268701af3a
|
4
|
+
data.tar.gz: bceb7f68344642b8f010b3e1ed5882afa4f4587fd7ebd1459635c2ca8954c3c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34e6e054bb02d891857461d62756f02f63b0f42491ed7768764c82a6f1b1c3ab9b8158878ac4a1ebf4d6482b4cec62be7de2168f2980c84f662725dca48d2490
|
7
|
+
data.tar.gz: 3104bcaf1ba5edf30ed53702248c0f52e02d50435812a5eb0b6158a7873f6e85f28109e680184f22c6f445fe319113a94ccb2e0ec362150cb8ac285d2805d3e9
|
data/CHANGELOG.md
CHANGED
@@ -1,24 +1,34 @@
|
|
1
|
-
<!-- latest_release 1.4.
|
2
|
-
## [v1.4.
|
1
|
+
<!-- latest_release 1.4.29 -->
|
2
|
+
## [v1.4.29](https://github.com/inspec/train/tree/v1.4.29) (2018-08-15)
|
3
3
|
|
4
4
|
#### Merged Pull Requests
|
5
|
-
-
|
5
|
+
- Add non_interactive support for SSH [#336](https://github.com/inspec/train/pull/336) ([marcparadise](https://github.com/marcparadise))
|
6
6
|
<!-- latest_release -->
|
7
7
|
|
8
|
-
<!-- release_rollup since=1.4.
|
9
|
-
### Changes since 1.4.
|
8
|
+
<!-- release_rollup since=1.4.25 -->
|
9
|
+
### Changes since 1.4.25 release
|
10
|
+
|
11
|
+
#### Features & Enhancements
|
12
|
+
- Pulls file credentials parsing out of Azure class [#324](https://github.com/inspec/train/pull/324) ([dmccown](https://github.com/dmccown)) <!-- 1.4.27 -->
|
10
13
|
|
11
14
|
#### Merged Pull Requests
|
12
|
-
-
|
15
|
+
- Add non_interactive support for SSH [#336](https://github.com/inspec/train/pull/336) ([marcparadise](https://github.com/marcparadise)) <!-- 1.4.29 -->
|
16
|
+
- Require Ruby 2.0 and allow net-ssh 5.0 [#334](https://github.com/inspec/train/pull/334) ([tas50](https://github.com/tas50)) <!-- 1.4.28 -->
|
17
|
+
- Modify checksum logic to use system binaries [#251](https://github.com/inspec/train/pull/251) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 1.4.26 -->
|
13
18
|
<!-- release_rollup -->
|
14
19
|
|
15
20
|
<!-- latest_stable_release -->
|
21
|
+
## [v1.4.25](https://github.com/inspec/train/tree/v1.4.25) (2018-08-01)
|
22
|
+
|
23
|
+
#### Merged Pull Requests
|
24
|
+
- Remove not needed google-cloud dependency (see #328) and correct GCP … [#329](https://github.com/inspec/train/pull/329) ([skpaterson](https://github.com/skpaterson))
|
25
|
+
<!-- latest_stable_release -->
|
26
|
+
|
16
27
|
## [v1.4.24](https://github.com/inspec/train/tree/v1.4.24) (2018-07-26)
|
17
28
|
|
18
29
|
#### Merged Pull Requests
|
19
30
|
- Add shallow_link_path to inspect symlink direct link [#309](https://github.com/inspec/train/pull/309) ([ColinHebert](https://github.com/ColinHebert))
|
20
31
|
- Retry SSH command on IOError (Cisco IOS specific) [#326](https://github.com/inspec/train/pull/326) ([jerryaldrichiii](https://github.com/jerryaldrichiii))
|
21
|
-
<!-- latest_stable_release -->
|
22
32
|
|
23
33
|
## [v1.4.22](https://github.com/inspec/train/tree/v1.4.22) (2018-07-16)
|
24
34
|
|
data/lib/train/file.rb
CHANGED
@@ -5,12 +5,10 @@
|
|
5
5
|
|
6
6
|
require 'train/file/local'
|
7
7
|
require 'train/file/remote'
|
8
|
-
require 'digest/sha2'
|
9
|
-
require 'digest/md5'
|
10
8
|
require 'train/extras/stat'
|
11
9
|
|
12
10
|
module Train
|
13
|
-
class File
|
11
|
+
class File # rubocop:disable Metrics/ClassLength
|
14
12
|
def initialize(backend, path, follow_symlink = true)
|
15
13
|
@backend = backend
|
16
14
|
@path = path || ''
|
@@ -48,22 +46,6 @@ module Train
|
|
48
46
|
:unknown
|
49
47
|
end
|
50
48
|
|
51
|
-
def md5sum
|
52
|
-
res = Digest::MD5.new
|
53
|
-
res.update(content)
|
54
|
-
res.hexdigest
|
55
|
-
rescue TypeError => _
|
56
|
-
nil
|
57
|
-
end
|
58
|
-
|
59
|
-
def sha256sum
|
60
|
-
res = Digest::SHA256.new
|
61
|
-
res.update(content)
|
62
|
-
res.hexdigest
|
63
|
-
rescue TypeError => _
|
64
|
-
nil
|
65
|
-
end
|
66
|
-
|
67
49
|
def source
|
68
50
|
if @follow_symlink
|
69
51
|
self.class.new(@backend, @path, false)
|
@@ -147,5 +129,82 @@ module Train
|
|
147
129
|
|
148
130
|
!mounted.nil? && !mounted.stdout.nil? && !mounted.stdout.empty?
|
149
131
|
end
|
132
|
+
|
133
|
+
def md5sum
|
134
|
+
# Skip processing rest of method if fallback method is selected
|
135
|
+
return perform_checksum_ruby(:md5) if defined?(@ruby_checksum_fallback)
|
136
|
+
|
137
|
+
checksum = if @backend.os.family == 'windows'
|
138
|
+
perform_checksum_windows(:md5)
|
139
|
+
else
|
140
|
+
@md5_command ||= case @backend.os.family
|
141
|
+
when 'darwin'
|
142
|
+
'md5 -r'
|
143
|
+
when 'solaris'
|
144
|
+
'digest -a md5'
|
145
|
+
else
|
146
|
+
'md5sum'
|
147
|
+
end
|
148
|
+
|
149
|
+
perform_checksum_unix(@md5_command)
|
150
|
+
end
|
151
|
+
|
152
|
+
checksum || perform_checksum_ruby(:md5)
|
153
|
+
end
|
154
|
+
|
155
|
+
def sha256sum
|
156
|
+
# Skip processing rest of method if fallback method is selected
|
157
|
+
return perform_checksum_ruby(:sha256) if defined?(@ruby_checksum_fallback)
|
158
|
+
|
159
|
+
checksum = if @backend.os.family == 'windows'
|
160
|
+
perform_checksum_windows(:sha256)
|
161
|
+
else
|
162
|
+
@sha256_command ||= case @backend.os.family
|
163
|
+
when 'darwin', 'hpux', 'qnx'
|
164
|
+
'shasum -a 256'
|
165
|
+
when 'solaris'
|
166
|
+
'digest -a sha256'
|
167
|
+
else
|
168
|
+
'sha256sum'
|
169
|
+
end
|
170
|
+
|
171
|
+
perform_checksum_unix(@sha256_command)
|
172
|
+
end
|
173
|
+
|
174
|
+
checksum || perform_checksum_ruby(:sha256)
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
def perform_checksum_unix(cmd)
|
180
|
+
res = @backend.run_command("#{cmd} #{@path}")
|
181
|
+
res.stdout.split(' ').first if res.exit_status == 0
|
182
|
+
end
|
183
|
+
|
184
|
+
def perform_checksum_windows(method)
|
185
|
+
cmd = "CertUtil -hashfile #{@path} #{method.to_s.upcase}"
|
186
|
+
res = @backend.run_command(cmd)
|
187
|
+
res.stdout.split("\r\n")[1].tr(' ', '') if res.exit_status == 0
|
188
|
+
end
|
189
|
+
|
190
|
+
# This pulls the content of the file to the machine running Train and uses
|
191
|
+
# Digest to perform the checksum. This is less efficient than using remote
|
192
|
+
# system binaries and can lead to incorrect results due to encoding.
|
193
|
+
def perform_checksum_ruby(method)
|
194
|
+
# This is used to skip attempting other checksum methods. If this is set
|
195
|
+
# then we know all other methods have failed.
|
196
|
+
@ruby_checksum_fallback = true
|
197
|
+
case method
|
198
|
+
when :md5
|
199
|
+
res = Digest::MD5.new
|
200
|
+
when :sha256
|
201
|
+
res = Digest::SHA256.new
|
202
|
+
end
|
203
|
+
|
204
|
+
res.update(content)
|
205
|
+
res.hexdigest
|
206
|
+
rescue TypeError => _
|
207
|
+
nil
|
208
|
+
end
|
150
209
|
end
|
151
210
|
end
|
@@ -3,9 +3,9 @@
|
|
3
3
|
require 'train/plugins'
|
4
4
|
require 'ms_rest_azure'
|
5
5
|
require 'azure_mgmt_resources'
|
6
|
-
require 'inifile'
|
7
6
|
require 'socket'
|
8
7
|
require 'timeout'
|
8
|
+
require 'train/transports/helpers/azure/file_credentials'
|
9
9
|
|
10
10
|
module Train::Transports
|
11
11
|
class Azure < Train.plugin(1)
|
@@ -23,7 +23,7 @@ module Train::Transports
|
|
23
23
|
@connection ||= Connection.new(@options)
|
24
24
|
end
|
25
25
|
|
26
|
-
class Connection < BaseConnection
|
26
|
+
class Connection < BaseConnection
|
27
27
|
attr_reader :options
|
28
28
|
|
29
29
|
def initialize(options)
|
@@ -38,7 +38,7 @@ module Train::Transports
|
|
38
38
|
@cache[:api_call] = {}
|
39
39
|
|
40
40
|
if @options[:client_secret].nil? && @options[:client_id].nil?
|
41
|
-
|
41
|
+
@options.merge!(Helpers::Azure::FileCredentials.parse(@options))
|
42
42
|
end
|
43
43
|
|
44
44
|
@options[:msi_port] = @options[:msi_port].to_i unless @options[:msi_port].nil?
|
@@ -149,40 +149,6 @@ module Train::Transports
|
|
149
149
|
rescue Timeout::Error
|
150
150
|
false
|
151
151
|
end
|
152
|
-
|
153
|
-
def parse_credentials_file # rubocop:disable Metrics/AbcSize
|
154
|
-
# If an AZURE_CRED_FILE environment variable has been specified set the
|
155
|
-
# the credentials file to that, otherwise set the one in home
|
156
|
-
azure_creds_file = @options[:credentials_file]
|
157
|
-
azure_creds_file = File.join(Dir.home, '.azure', 'credentials') if azure_creds_file.nil?
|
158
|
-
return unless File.readable?(azure_creds_file)
|
159
|
-
|
160
|
-
credentials = IniFile.load(File.expand_path(azure_creds_file))
|
161
|
-
if @options[:subscription_id]
|
162
|
-
id = @options[:subscription_id]
|
163
|
-
elsif !ENV['AZURE_SUBSCRIPTION_NUMBER'].nil?
|
164
|
-
subscription_number = ENV['AZURE_SUBSCRIPTION_NUMBER'].to_i
|
165
|
-
|
166
|
-
# Check that the specified index is not greater than the number of subscriptions
|
167
|
-
if subscription_number > credentials.sections.length
|
168
|
-
raise format(
|
169
|
-
'Your credentials file only contains %s subscriptions. You specified number %s.',
|
170
|
-
@credentials.sections.length,
|
171
|
-
subscription_number,
|
172
|
-
)
|
173
|
-
end
|
174
|
-
id = credentials.sections[subscription_number - 1]
|
175
|
-
else
|
176
|
-
raise 'Multiple credentials detected, please set the AZURE_SUBSCRIPTION_ID environment variable.' if credentials.sections.count > 1
|
177
|
-
id = credentials.sections[0]
|
178
|
-
end
|
179
|
-
|
180
|
-
raise "No credentials found for subscription number #{id}" if credentials.sections.empty? || credentials[id].empty?
|
181
|
-
@options[:subscription_id] = id
|
182
|
-
@options[:tenant_id] = credentials[id]['tenant_id']
|
183
|
-
@options[:client_id] = credentials[id]['client_id']
|
184
|
-
@options[:client_secret] = credentials[id]['client_secret']
|
185
|
-
end
|
186
152
|
end
|
187
153
|
end
|
188
154
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'inifile'
|
4
|
+
require 'train/transports/helpers/azure/file_parser'
|
5
|
+
require 'train/transports/helpers/azure/subscription_number_file_parser'
|
6
|
+
require 'train/transports/helpers/azure/subscription_id_file_parser'
|
7
|
+
|
8
|
+
module Train::Transports
|
9
|
+
module Helpers
|
10
|
+
module Azure
|
11
|
+
class FileCredentials
|
12
|
+
DEFAULT_FILE = ::File.join(Dir.home, '.azure', 'credentials')
|
13
|
+
|
14
|
+
def self.parse(subscription_id: nil, credentials_file: DEFAULT_FILE, **_)
|
15
|
+
return {} unless ::File.readable?(credentials_file)
|
16
|
+
credentials = IniFile.load(::File.expand_path(credentials_file))
|
17
|
+
subscription_id = parser(subscription_id, ENV['AZURE_SUBSCRIPTION_NUMBER'], credentials).subscription_id
|
18
|
+
creds(subscription_id, credentials)
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.parser(subscription_id, subscription_number, credentials)
|
22
|
+
if subscription_id
|
23
|
+
SubscriptionIdFileParser.new(subscription_id, credentials)
|
24
|
+
elsif !subscription_number.nil?
|
25
|
+
SubscriptionNumberFileParser.new(subscription_number.to_i, credentials)
|
26
|
+
else
|
27
|
+
FileParser.new(credentials)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.creds(subscription_id, credentials)
|
32
|
+
{
|
33
|
+
subscription_id: subscription_id,
|
34
|
+
tenant_id: credentials[subscription_id]['tenant_id'],
|
35
|
+
client_id: credentials[subscription_id]['client_id'],
|
36
|
+
client_secret: credentials[subscription_id]['client_secret'],
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Train::Transports
|
4
|
+
module Helpers
|
5
|
+
module Azure
|
6
|
+
class FileParser
|
7
|
+
def initialize(credentials)
|
8
|
+
@credentials = credentials
|
9
|
+
|
10
|
+
validate!
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate!
|
14
|
+
return if @credentials.sections.count == 1
|
15
|
+
|
16
|
+
raise 'Credentials file must have one entry. Check your credentials file. If you have more than one entry set AZURE_SUBSCRIPTION_ID environment variable.'
|
17
|
+
end
|
18
|
+
|
19
|
+
def subscription_id
|
20
|
+
@subscription_id ||= @credentials.sections[0]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Train::Transports
|
4
|
+
module Helpers
|
5
|
+
module Azure
|
6
|
+
class SubscriptionIdFileParser
|
7
|
+
attr_reader :subscription_id
|
8
|
+
|
9
|
+
def initialize(subscription_id, credentials)
|
10
|
+
@subscription_id = subscription_id
|
11
|
+
@credentials = credentials
|
12
|
+
|
13
|
+
validate!
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate!
|
17
|
+
if @credentials.sections.empty? || @credentials[subscription_id].empty?
|
18
|
+
raise "No credentials found for subscription number #{subscription_id}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Train::Transports
|
4
|
+
module Helpers
|
5
|
+
module Azure
|
6
|
+
class SubscriptionNumberFileParser
|
7
|
+
def initialize(index, credentials)
|
8
|
+
@index = index
|
9
|
+
@credentials = credentials
|
10
|
+
|
11
|
+
validate!
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate!
|
15
|
+
if @index == 0
|
16
|
+
raise 'Index must be greater than 0.'
|
17
|
+
end
|
18
|
+
|
19
|
+
if @index > @credentials.sections.length
|
20
|
+
raise "Your credentials file only contains #{@credentials.sections.length} subscriptions. You specified number #{@index}."
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def subscription_id
|
25
|
+
@subscription_id ||= @credentials.sections[@index - 1]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/train/transports/ssh.rb
CHANGED
@@ -62,6 +62,7 @@ module Train::Transports
|
|
62
62
|
option :bastion_host, default: nil
|
63
63
|
option :bastion_user, default: 'root'
|
64
64
|
option :bastion_port, default: 22
|
65
|
+
option :non_interactive, default: false
|
65
66
|
|
66
67
|
option :compression_level do |opts|
|
67
68
|
# on nil or false: set compression level to 0
|
@@ -162,9 +163,9 @@ module Train::Transports
|
|
162
163
|
bastion_host: opts[:bastion_host],
|
163
164
|
bastion_user: opts[:bastion_user],
|
164
165
|
bastion_port: opts[:bastion_port],
|
166
|
+
non_interactive: opts[:non_interactive],
|
165
167
|
transport_options: opts,
|
166
168
|
}
|
167
|
-
|
168
169
|
# disable host key verification. The hash key to use
|
169
170
|
# depends on the version of net-ssh in use.
|
170
171
|
connection_options[verify_host_key_option] = false
|
@@ -69,6 +69,7 @@ class Train::Transports::SSH
|
|
69
69
|
args = %w{ -o UserKnownHostsFile=/dev/null }
|
70
70
|
args += %w{ -o StrictHostKeyChecking=no }
|
71
71
|
args += %w{ -o IdentitiesOnly=yes } if options[:keys]
|
72
|
+
args += %w{ -o BatchMode=yes } if options[:non_interactive]
|
72
73
|
args += %W( -o LogLevel=#{level} )
|
73
74
|
args += %W( -o ForwardAgent=#{fwd_agent} ) if options.key?(:forward_agent)
|
74
75
|
Array(options[:keys]).each do |ssh_key|
|
data/lib/train/version.rb
CHANGED
@@ -42,11 +42,11 @@ describe 'file interface' do
|
|
42
42
|
file.link_path.must_be_nil
|
43
43
|
end
|
44
44
|
|
45
|
-
it 'has
|
45
|
+
it 'has the correct md5sum' do
|
46
46
|
file.md5sum.must_equal('d41d8cd98f00b204e9800998ecf8427e')
|
47
47
|
end
|
48
48
|
|
49
|
-
it 'has
|
49
|
+
it 'has the correct sha256sum' do
|
50
50
|
file.sha256sum.must_equal('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')
|
51
51
|
end
|
52
52
|
|
@@ -37,12 +37,12 @@ describe 'file interface' do
|
|
37
37
|
file.content.must_be_nil
|
38
38
|
end
|
39
39
|
|
40
|
-
it '
|
41
|
-
file.md5sum.
|
40
|
+
it 'raises an error if md5sum is attempted' do
|
41
|
+
proc { file.md5sum }.must_raise RuntimeError
|
42
42
|
end
|
43
43
|
|
44
|
-
it '
|
45
|
-
file.sha256sum.
|
44
|
+
it 'raises an error if sha256sum is attempted' do
|
45
|
+
proc { file.sha256sum }.must_raise RuntimeError
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
@@ -44,12 +44,12 @@ describe 'file interface' do
|
|
44
44
|
file.link_path.must_be_nil
|
45
45
|
end
|
46
46
|
|
47
|
-
it '
|
48
|
-
file.md5sum.
|
47
|
+
it 'raises an error if md5sum is attempted' do
|
48
|
+
proc { file.md5sum }.must_raise RuntimeError
|
49
49
|
end
|
50
50
|
|
51
|
-
it '
|
52
|
-
file.sha256sum.
|
51
|
+
it 'raises an error if sha256sum is attempted' do
|
52
|
+
proc { file.sha256sum }.must_raise RuntimeError
|
53
53
|
end
|
54
54
|
|
55
55
|
it 'has a modified time' do
|