train 1.4.25 → 1.4.29
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.
- 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
|