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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e20385227ab1b9267ab2d34ca6880657f0d26682b66ca2a10f885ee5f349ce6
4
- data.tar.gz: f675b5c27b234f25ad7d3d73a5572422be938f1bb0b59f9e7ef970df665807c1
3
+ metadata.gz: 65be3294011740eca689d8b5de1273f14f37d5189eae2a29111a4d268701af3a
4
+ data.tar.gz: bceb7f68344642b8f010b3e1ed5882afa4f4587fd7ebd1459635c2ca8954c3c6
5
5
  SHA512:
6
- metadata.gz: 58c5b580ea0f04cbc602e52742bdc91bab4c98f41fab640bda1ea60b704cfc6b698022f44b18243ca9e8e317c88801816e302f894c30b1a453e4254f6730475a
7
- data.tar.gz: 1d1d9c27431bdbf2c6fbacbc444065c5ac7add670febfb52b8e7eb17ca7d941961effe6d49619204ba75943d8173fbe049fec5bc9560f9dabca3511afb70fefd
6
+ metadata.gz: 34e6e054bb02d891857461d62756f02f63b0f42491ed7768764c82a6f1b1c3ab9b8158878ac4a1ebf4d6482b4cec62be7de2168f2980c84f662725dca48d2490
7
+ data.tar.gz: 3104bcaf1ba5edf30ed53702248c0f52e02d50435812a5eb0b6158a7873f6e85f28109e680184f22c6f445fe319113a94ccb2e0ec362150cb8ac285d2805d3e9
@@ -1,24 +1,34 @@
1
- <!-- latest_release 1.4.25 -->
2
- ## [v1.4.25](https://github.com/inspec/train/tree/v1.4.25) (2018-08-01)
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
- - Remove not needed google-cloud dependency (see #328) and correct GCP … [#329](https://github.com/inspec/train/pull/329) ([skpaterson](https://github.com/skpaterson))
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.24 -->
9
- ### Changes since 1.4.24 release
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
- - Remove not needed google-cloud dependency (see #328) and correct GCP … [#329](https://github.com/inspec/train/pull/329) ([skpaterson](https://github.com/skpaterson)) <!-- 1.4.25 -->
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
 
@@ -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 # rubocop:disable Metrics/ClassLength
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
- parse_credentials_file
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
@@ -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|
@@ -3,5 +3,5 @@
3
3
  # Author:: Dominik Richter (<dominik.richter@gmail.com>)
4
4
 
5
5
  module Train
6
- VERSION = '1.4.25'.freeze
6
+ VERSION = '1.4.29'.freeze
7
7
  end
@@ -42,11 +42,11 @@ describe 'file interface' do
42
42
  file.link_path.must_be_nil
43
43
  end
44
44
 
45
- it 'has no md5sum' do
45
+ it 'has the correct md5sum' do
46
46
  file.md5sum.must_equal('d41d8cd98f00b204e9800998ecf8427e')
47
47
  end
48
48
 
49
- it 'has no sha256sum' do
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 'has an md5sum' do
41
- file.md5sum.must_be_nil
40
+ it 'raises an error if md5sum is attempted' do
41
+ proc { file.md5sum }.must_raise RuntimeError
42
42
  end
43
43
 
44
- it 'has an sha256sum' do
45
- file.sha256sum.must_be_nil
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 'has an md5sum' do
48
- file.md5sum.must_be_nil
47
+ it 'raises an error if md5sum is attempted' do
48
+ proc { file.md5sum }.must_raise RuntimeError
49
49
  end
50
50
 
51
- it 'has an sha256sum' do
52
- file.sha256sum.must_be_nil
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