test-kitchen 3.1.0 → 3.6.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc5e05dc50e7052b2002a94e950f5691d953c78d5e408b80a0f7f977ab63eb35
4
- data.tar.gz: 43bc1979cc77277d4a3219f82d8fead18e7aa3cf068fb7484f5176ce422c8767
3
+ metadata.gz: ecd483d58546fb48da8bb5a4e5c448b7cc2f8b7d2c0082d0df4b023c380a2594
4
+ data.tar.gz: cb42f2dae4d33d84ff8515a7412d7e1d14c358504f66429b21465bef7714e48b
5
5
  SHA512:
6
- metadata.gz: 12453b3cbf1ef1dfb49b683667fca52502b10a888e9cf953d58c22589ce99dffc9864d550463c80341049f7073aa3c3fb98eb76c4e432aa266956b6a0a86c332
7
- data.tar.gz: bc19f713f60acc0b5437c5b29ab241255a7a82de2b206ba714596bf2a251c0e9a3c21bc152a18e171461ca24d9291098b55baeec474e495f46824782487350b8
6
+ metadata.gz: 8adf00afe37d4b31dec9b6acca4aee2aa858b265db0fee4f0c0fac8407691f452e7d487d45ec5f72e816d0799060affcaf31d8885597ade1a9d40dc59f6056bc
7
+ data.tar.gz: bc82343bf133cbce4c9c5096d1d88a9bb7ab56cf619d289f71008388757d553c43432f3188d72ddc87c917212ce6a6278ee0d81a64b8f7d688341e387da2949f
data/Gemfile CHANGED
@@ -1,21 +1,26 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- # Specify your gem"s dependencies in test-kitchen.gemspec
4
3
  gemspec
5
4
 
5
+ group :test do
6
+ gem "rake"
7
+ gem "rb-readline"
8
+ gem "aruba", ">= 0.11", "< 3.0"
9
+ gem "countloc", "~> 0.4"
10
+ gem "cucumber", ">= 2.1", "< 8.1"
11
+ gem "fakefs", "~> 2.0"
12
+ gem "maruku", "~> 0.6"
13
+ gem "minitest", "~> 5.3", "< 5.16"
14
+ gem "mocha", "~> 2.0"
15
+ end
16
+
6
17
  group :integration do
7
18
  gem "berkshelf"
8
- gem "kitchen-inspec"
9
19
  gem "kitchen-dokken"
20
+ gem "kitchen-inspec"
10
21
  gem "kitchen-vagrant"
11
22
  end
12
23
 
13
- group :debug do
14
- gem "pry", "~>0.12"
15
- gem "pry-byebug"
16
- gem "pry-stack_explorer"
17
- end
18
-
19
24
  group :chefstyle do
20
- gem "chefstyle", "2.1.0"
21
- end
25
+ gem "chefstyle", "2.2.3"
26
+ end
data/lib/kitchen/cli.rb CHANGED
@@ -44,7 +44,7 @@ module Kitchen
44
44
  action: task,
45
45
  help: -> { help(task) },
46
46
  config: @config,
47
- shell: shell,
47
+ shell:,
48
48
  }.merge(additional_options)
49
49
 
50
50
  str_const = Thor::Util.camel_case(command)
@@ -33,7 +33,7 @@ module Kitchen
33
33
  loader = record_failure { load_loader }
34
34
 
35
35
  puts YAML.dump(Kitchen::Diagnostic.new(
36
- loader: loader, instances: instances, plugins: plugins?
36
+ loader:, instances:, plugins: plugins?
37
37
  ).read)
38
38
  end
39
39
 
@@ -216,11 +216,11 @@ module Kitchen
216
216
  /^win/i.match?(platform) ? "winrm" : Transport::DEFAULT_PLUGIN
217
217
  end,
218
218
  },
219
- kitchen_root: kitchen_root,
220
- test_base_path: test_base_path,
221
- log_level: log_level,
222
- log_overwrite: log_overwrite,
223
- debug: debug,
219
+ kitchen_root:,
220
+ test_base_path:,
221
+ log_level:,
222
+ log_overwrite:,
223
+ debug:,
224
224
  }
225
225
  end
226
226
 
@@ -250,8 +250,8 @@ module Kitchen
250
250
  driver: new_driver(suite, platform),
251
251
  lifecycle_hooks: new_lifecycle_hooks(suite, platform, sf),
252
252
  logger: new_instance_logger(suite, platform, index),
253
- suite: suite,
254
- platform: platform,
253
+ suite:,
254
+ platform:,
255
255
  provisioner: new_provisioner(suite, platform),
256
256
  transport: new_transport(suite, platform),
257
257
  verifier: new_verifier(suite, platform),
@@ -275,7 +275,7 @@ module Kitchen
275
275
  color: Color::COLORS[index % Color::COLORS.size].to_sym,
276
276
  logdev: log_location,
277
277
  level: Util.to_logger_level(log_level),
278
- log_overwrite: log_overwrite,
278
+ log_overwrite:,
279
279
  progname: name,
280
280
  colorize: @colorize
281
281
  )
@@ -241,7 +241,7 @@ module Kitchen
241
241
  #{deprecated_config.keys.join("\n")}
242
242
  Run 'kitchen doctor' for details.
243
243
  MSG
244
- warn(warning)
244
+ Error.warn_on_stderr(warning)
245
245
 
246
246
  # Set global var that the deprecation message has been printed
247
247
  @@has_been_warned_of_deprecations = true
@@ -70,7 +70,7 @@ module Kitchen
70
70
  def converge(state) # rubocop:disable Metrics/AbcSize
71
71
  provisioner = instance.provisioner
72
72
  provisioner.create_sandbox
73
- sandbox_dirs = Util.list_directory(provisioner.sandbox_path)
73
+ sandbox_dirs = provisioner.sandbox_dirs
74
74
 
75
75
  instance.transport.connection(backcompat_merged_state(state)) do |conn|
76
76
  conn.execute(env_cmd(provisioner.install_command))
@@ -299,7 +299,7 @@ module Kitchen
299
299
  # @param options [Hash] configuration hash (default: `{}`)
300
300
  # @api private
301
301
  def wait_for_sshd(hostname, username = nil, options = {})
302
- pseudo_state = { hostname: hostname }
302
+ pseudo_state = { hostname: }
303
303
  pseudo_state[:username] = username if username
304
304
  pseudo_state.merge!(options)
305
305
 
@@ -94,6 +94,19 @@ module Kitchen
94
94
  "".center(22, "-"),
95
95
  ]
96
96
  end
97
+
98
+ # Log a warn message on STDERR device.
99
+ # This will help to distinguish between the errors and
100
+ # output when parsing the output from the commands like
101
+ # kitchen diagnose.
102
+ #
103
+ # @params lines [Array<String>] Array of lines that needs to be printed
104
+ def self.warn_on_stderr(lines)
105
+ Array(lines).each do |line|
106
+ line = Color.colorize(line, :blue) if Kitchen.tty?
107
+ $stderr.puts(line)
108
+ end
109
+ end
97
110
  end
98
111
 
99
112
  # Base exception class from which all Kitchen exceptions derive. This class
@@ -1,3 +1,5 @@
1
+ require_relative "../platform_filter"
2
+
1
3
  module Kitchen
2
4
  class LifecycleHook
3
5
  class Base
@@ -59,14 +61,14 @@ module Kitchen
59
61
  lifecycle_hooks.state_file
60
62
  end
61
63
 
62
- # @return [Array<String>] names of excluded platforms
64
+ # @return [Array<PlatformFilter>] names of excluded platforms
63
65
  def excludes
64
- @excludes ||= hook.fetch(:excludes, [])
66
+ @excludes ||= PlatformFilter.convert(hook.fetch(:excludes, []))
65
67
  end
66
68
 
67
- # @return [Array<String>] names of only included platforms
69
+ # @return [Array<PlatformFilter>] names of only included platforms
68
70
  def includes
69
- @includes ||= hook.fetch(:includes, [])
71
+ @includes ||= PlatformFilter.convert(hook.fetch(:includes, []))
70
72
  end
71
73
 
72
74
  # @return [String]
@@ -38,7 +38,7 @@ module Kitchen
38
38
  config[:kitchen_root]
39
39
  end
40
40
  # Build the options for mixlib-shellout.
41
- opts = {}.merge(user).merge(cwd: cwd, environment: environment)
41
+ opts = {}.merge(user).merge(cwd:, environment:)
42
42
  run_command(command, opts)
43
43
  end
44
44
 
@@ -46,6 +46,8 @@ module Kitchen
46
46
  run(phase, :pre)
47
47
  yield
48
48
  run(phase, :post)
49
+ ensure
50
+ run(phase, :finally)
49
51
  end
50
52
 
51
53
  # @return [Kitchen::StateFile]
@@ -56,7 +58,7 @@ module Kitchen
56
58
  # Execute a specific lifecycle hook.
57
59
  #
58
60
  # @param phase [String] Lifecycle phase which is being executed.
59
- # @param hook_timing [Symbol] `:pre` or `:post` to indicate which hook to run.
61
+ # @param hook_timing [Symbol] `:pre`, `:post`, or `:finally` to indicate which hook to run.
60
62
  # @return [void]
61
63
  def run(phase, hook_timing)
62
64
  # Yes this has to be a symbol because of how data munger works.
@@ -352,14 +352,7 @@ module Kitchen
352
352
  def parse_yaml_string(string, file_name)
353
353
  return {} if string.nil? || string.empty?
354
354
 
355
- result =
356
- if Gem::Requirement.new(">= 3.1.0").satisfied_by?(Gem::Version.new(Psych::VERSION))
357
- # ruby >= 2.6.0
358
- ::YAML.safe_load(string, permitted_classes: [Symbol], permitted_symbols: [], aliases: true) || {}
359
- else
360
- # ruby < 2.6.0
361
- ::YAML.safe_load(string, [Symbol], [], true) || {}
362
- end
355
+ result = ::YAML.safe_load(string, permitted_classes: [Symbol], permitted_symbols: [], aliases: true) || {}
363
356
  unless result.is_a?(Hash)
364
357
  raise UserError, "Error parsing #{file_name} as YAML " \
365
358
  "(Result of parse was not a Hash, but was a #{result.class}).\n" \
@@ -58,7 +58,7 @@ module Kitchen
58
58
  #
59
59
  # @return [Hash] a diagnostic hash
60
60
  def diagnose
61
- { os_type: os_type, shell_type: shell_type }
61
+ { os_type:, shell_type: }
62
62
  end
63
63
  end
64
64
  end
@@ -0,0 +1,72 @@
1
+ #
2
+ # Author:: Baptiste Courtois (<b.courtois@criteo.com>)
3
+ #
4
+ # Copyright (C) 2021, Baptiste Courtois
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ module Kitchen
19
+ # A wrapper on Regexp and strings to mix them in platform filters.
20
+ #
21
+ # This should handle backward compatibility in most cases were
22
+ # platform are matched against a filters array using Array.include?
23
+ #
24
+ # This wrapper does not work if filters arrays are converted to Set.
25
+ #
26
+ # @author Baptiste Courtois <b.courtois@criteo.com>
27
+ class PlatformFilter
28
+ # Pattern used to determine whether a filter should be handled as a Regexp
29
+ REGEXP_LIKE_PATTERN = %r{^/(?<pattern>.*)/(?<options>[ix]*)$}
30
+
31
+ # Converts platform filters into an array of PlatformFilter handling both strings and Regexp.
32
+ # A string "looks-like" a regexp if it starts by / and end by / + Regexp options i or x
33
+ #
34
+ # @return [Array] filters with regexp-like string converted to PlatformRegexpFilter
35
+ def self.convert(filters)
36
+ ::Kernel.Array(filters).map do |filter|
37
+ if (match = filter.match(REGEXP_LIKE_PATTERN))
38
+ options = match["options"].include?("i") ? ::Regexp::IGNORECASE : 0
39
+ options |= ::Regexp::EXTENDED if match["options"].include?("x")
40
+ filter = ::Regexp.new(match["pattern"], options)
41
+ end
42
+ new(filter)
43
+ end
44
+ end
45
+
46
+ # @return [Regexp] value of this filter
47
+ attr_reader :value
48
+
49
+ # Constructs a new filter.
50
+ #
51
+ # @param [Regexp,String] value of the filter
52
+ def initialize(value)
53
+ raise ::ArgumentError, "PlatformFilter#new requires value to be a String or a Regexp" unless value.is_a?(::Regexp) || value.is_a?(::String)
54
+
55
+ @value = value
56
+ end
57
+
58
+ # Override of the equality operator to check whether the wrapped Regexp match the given object.
59
+ #
60
+ # @param [Object] other object to compare to
61
+ # @return [Boolean] whether the objects are equal or the wrapped Regexp matches the given string or symbol
62
+ def ==(other)
63
+ if @value.is_a?(::Regexp) && (other.is_a?(::String) || other.is_a?(::Symbol))
64
+ @value =~ other
65
+ else
66
+ other == @value
67
+ end
68
+ end
69
+
70
+ alias eq? ==
71
+ end
72
+ end
@@ -54,7 +54,6 @@ module Kitchen
54
54
  @serial_actions ||= []
55
55
  @serial_actions += methods
56
56
  end
57
-
58
57
  end
59
58
  end
60
59
  end
@@ -70,7 +70,6 @@ module Kitchen
70
70
  # rubocop:disable Metrics/AbcSize
71
71
  def call(state)
72
72
  create_sandbox
73
- sandbox_dirs = Util.list_directory(sandbox_path)
74
73
 
75
74
  instance.transport.connection(state) do |conn|
76
75
  config[:uploads].to_h.each do |locals, remote|
@@ -182,6 +181,13 @@ module Kitchen
182
181
  "trying to access the path."
183
182
  end
184
183
 
184
+ # Returns the list of items in the sandbox directory
185
+ #
186
+ # @return [String] path of items in the sandbox directory
187
+ def sandbox_dirs
188
+ Util.list_directory(sandbox_path)
189
+ end
190
+
185
191
  # Deletes the sandbox path. Without calling this method, the sandbox path
186
192
  # will persist after the process terminates. In other words, cleanup is
187
193
  # explicit. This method is safe to call multiple times.
@@ -287,16 +287,17 @@ module Kitchen
287
287
  def update_dna_for_policyfile
288
288
  policy = Chef::Policyfile.new(
289
289
  policyfile, sandbox_path,
290
- logger: logger,
290
+ logger:,
291
291
  always_update: config[:always_update_cookbooks],
292
- policy_group: policy_group
292
+ policy_group:,
293
+ license: config[:chef_license]
293
294
  )
294
295
  Kitchen.mutex.synchronize do
295
296
  policy.compile
296
297
  end
297
298
  policy_name = JSON.parse(IO.read(policy.lockfile))["name"]
298
299
  policy_group = config[:policy_group] || "local"
299
- config[:attributes].merge(policy_name: policy_name, policy_group: policy_group)
300
+ config[:attributes].merge(policy_name:, policy_group:)
300
301
  end
301
302
 
302
303
  # Performs a Policyfile cookbook resolution inside a common mutex.
@@ -306,9 +307,10 @@ module Kitchen
306
307
  Kitchen.mutex.synchronize do
307
308
  Chef::Policyfile.new(
308
309
  policyfile, sandbox_path,
309
- logger: logger,
310
+ logger:,
310
311
  always_update: config[:always_update_cookbooks],
311
- policy_group: config[:policy_group]
312
+ policy_group: config[:policy_group],
313
+ license: config[:chef_license]
312
314
  ).resolve
313
315
  end
314
316
  end
@@ -319,7 +321,7 @@ module Kitchen
319
321
  def resolve_with_berkshelf
320
322
  Kitchen.mutex.synchronize do
321
323
  Chef::Berkshelf.new(berksfile, tmpbooks_dir,
322
- logger: logger,
324
+ logger:,
323
325
  always_update: config[:always_update_cookbooks]).resolve
324
326
  end
325
327
  end
@@ -41,12 +41,13 @@ module Kitchen
41
41
  # cookbooks
42
42
  # @param logger [Kitchen::Logger] a logger to use for output, defaults
43
43
  # to `Kitchen.logger`
44
- def initialize(policyfile, path, logger: Kitchen.logger, always_update: false, policy_group: nil)
44
+ def initialize(policyfile, path, license: nil, logger: Kitchen.logger, always_update: false, policy_group: nil)
45
45
  @policyfile = policyfile
46
46
  @path = path
47
47
  @logger = logger
48
48
  @always_update = always_update
49
49
  @policy_group = policy_group
50
+ @license = license
50
51
  end
51
52
 
52
53
  # Loads the library code required to use the resolver.
@@ -62,10 +63,10 @@ module Kitchen
62
63
  def resolve
63
64
  if policy_group
64
65
  info("Exporting cookbook dependencies from Policyfile #{path} with policy_group #{policy_group} using `#{cli_path} export`...")
65
- run_command("#{cli_path} export #{escape_path(policyfile)} #{escape_path(path)} --policy_group #{policy_group} --force")
66
+ run_command("#{cli_path} export #{escape_path(policyfile)} #{escape_path(path)} --policy_group #{policy_group} --force --chef-license #{license}")
66
67
  else
67
68
  info("Exporting cookbook dependencies from Policyfile #{path} using `#{cli_path} export`...")
68
- run_command("#{cli_path} export #{escape_path(policyfile)} #{escape_path(path)} --force")
69
+ run_command("#{cli_path} export #{escape_path(policyfile)} #{escape_path(path)} --force --chef-license #{license}")
69
70
  end
70
71
  end
71
72
 
@@ -77,11 +78,11 @@ module Kitchen
77
78
  else
78
79
  info("Policy lock file doesn't exist, running `#{cli_path} install` for Policyfile #{policyfile}...")
79
80
  end
80
- run_command("#{cli_path} install #{escape_path(policyfile)}")
81
+ run_command("#{cli_path} install #{escape_path(policyfile)} --chef-license #{license}")
81
82
 
82
83
  if always_update
83
84
  info("Updating policy lock using `#{cli_path} update`")
84
- run_command("#{cli_path} update #{escape_path(policyfile)}")
85
+ run_command("#{cli_path} update #{escape_path(policyfile)} --chef-license #{license}")
85
86
  end
86
87
  end
87
88
 
@@ -114,6 +115,10 @@ module Kitchen
114
115
  # @api private
115
116
  attr_reader :policy_group
116
117
 
118
+ # @return [String] name of the chef_license
119
+ # @api private
120
+ attr_reader :license
121
+
117
122
  # Escape spaces in a path in way that works with both Sh (Unix) and
118
123
  # Windows.
119
124
  #
@@ -152,11 +157,10 @@ module Kitchen
152
157
  def no_cli_found_error
153
158
  @logger.fatal("The `chef` or `chef-cli` executables cannot be found in your " \
154
159
  "PATH. Ensure you have installed Chef Workstation " \
155
- "from https://downloads.chef.io and that your PATH " \
160
+ "from https://www.chef.io/downloads/ and that your PATH " \
156
161
  "setting includes the path to the `chef` or `chef-cli` commands.")
157
162
  raise UserError, "Could not find the chef or chef-cli executables in your PATH."
158
163
  end
159
-
160
164
  end
161
165
  end
162
166
  end
@@ -69,7 +69,7 @@ module Kitchen
69
69
  README.* VERSION metadata.{json,rb} attributes.rb recipe.rb
70
70
  attributes/**/* definitions/**/* files/**/* libraries/**/*
71
71
  providers/**/* recipes/**/* resources/**/* templates/**/*
72
- ohai/**/*
72
+ ohai/**/* compliance/**/*
73
73
  ).join(",")
74
74
  # to ease upgrades, allow the user to turn deprecation warnings into errors
75
75
  default_config :deprecations_as_errors, false
@@ -210,7 +210,7 @@ module Kitchen
210
210
 
211
211
  deprecate_config_for :install_msi_url, Util.outdent!(<<-MSG)
212
212
  The 'install_msi_url' will be relaced by the 'download_url' attribute.
213
- 'download_url' will be applied to Bourne and Powershell download scripts.
213
+ 'download_url' will be applied to Bourne and PowerShell download scripts.
214
214
 
215
215
  Note: 'product_name' must be set in order to use 'download_url'
216
216
  until 'product_name' replaces 'require_chef_omnibus' as the default.
@@ -347,7 +347,7 @@ module Kitchen
347
347
  omnibus_url: config[:chef_omnibus_url],
348
348
  project: project.nil? ? nil : project[1],
349
349
  install_flags: config[:chef_omnibus_install_options],
350
- sudo_command: sudo_command,
350
+ sudo_command:,
351
351
  }.tap do |opts|
352
352
  opts[:root] = config[:chef_omnibus_root] if config.key? :chef_omnibus_root
353
353
  %i{install_msi_url http_proxy https_proxy}.each do |key|
@@ -482,10 +482,10 @@ module Kitchen
482
482
  super
483
483
  if File.exist?(policyfile)
484
484
  debug("Policyfile found at #{policyfile}, using Policyfile to resolve cookbook dependencies")
485
- Chef::Policyfile.load!(logger: logger)
485
+ Chef::Policyfile.load!(logger:)
486
486
  elsif File.exist?(berksfile)
487
487
  debug("Berksfile found at #{berksfile}, using Berkshelf to resolve cookbook dependencies")
488
- Chef::Berkshelf.load!(logger: logger)
488
+ Chef::Berkshelf.load!(logger:)
489
489
  end
490
490
  end
491
491
 
@@ -681,25 +681,42 @@ module Kitchen
681
681
  chef_cmds(base_cmd).join(separator)
682
682
  end
683
683
 
684
- # Gives an array of command
684
+ # Gives an array of commands
685
685
  # @api private
686
686
  def chef_cmds(base_cmd)
687
- cmd = prefix_command(wrap_shell_code(
688
- [base_cmd, *chef_args(config_filename), last_exit_code].join(" ")
689
- .tap { |str| str.insert(0, reload_ps1_path) if windows_os? }
690
- ))
691
-
692
- cmds = [cmd].cycle(config[:multiple_converge].to_i).to_a
693
-
694
- if config[:enforce_idempotency]
695
- idempotent_cmd = prefix_command(wrap_shell_code(
696
- [base_cmd, *chef_args("client_no_updated_resources.rb"), last_exit_code].join(" ")
697
- .tap { |str| str.insert(0, reload_ps1_path) if windows_os? }
698
- ))
699
- cmds[-1] = idempotent_cmd
687
+ cmds = []
688
+ num_converges = config[:multiple_converge].to_i
689
+ idempotency = config[:enforce_idempotency]
690
+
691
+ # Execute Chef Client n-1 times, without exiting
692
+ (num_converges - 1).times do
693
+ cmds << wrapped_chef_cmd(base_cmd, config_filename)
700
694
  end
695
+
696
+ # Append another execution with Windows specific Exit code helper or (for
697
+ # idempotency check) a specific config file which assures no changed resources.
698
+ cmds << unless idempotency
699
+ wrapped_chef_cmd(base_cmd, config_filename, append: last_exit_code)
700
+ else
701
+ wrapped_chef_cmd(base_cmd, "client_no_updated_resources.rb", append: last_exit_code)
702
+ end
701
703
  cmds
702
704
  end
705
+
706
+ # Concatenate all arguments and wrap it with shell-specifics
707
+ # @api private
708
+ def wrapped_chef_cmd(base_cmd, configfile, append: "")
709
+ args = []
710
+
711
+ args << base_cmd
712
+ args << chef_args(configfile)
713
+ args << append
714
+
715
+ shell_cmd = args.flatten.join(" ")
716
+ shell_cmd = shell_cmd.prepend(reload_ps1_path) if windows_os?
717
+
718
+ prefix_command(wrap_shell_code(shell_cmd))
719
+ end
703
720
  end
704
721
  end
705
722
  end
@@ -151,7 +151,6 @@ module Kitchen
151
151
  info("No provisioner script file specified, skipping")
152
152
  end
153
153
  end
154
-
155
154
  end
156
155
  end
157
156
  end
data/lib/kitchen/ssh.rb CHANGED
@@ -23,6 +23,7 @@ require "socket" unless defined?(Socket)
23
23
 
24
24
  require_relative "errors"
25
25
  require_relative "login_command"
26
+ require_relative "util"
26
27
 
27
28
  module Kitchen
28
29
  # Wrapped exception for any internally raised SSH-related errors.
@@ -75,7 +76,9 @@ module Kitchen
75
76
  # @param cmd [String] command string to execute
76
77
  # @raise [SSHFailed] if the command does not exit with a 0 code
77
78
  def exec(cmd)
78
- logger.debug("[SSH] #{self} (#{cmd})")
79
+ string_to_mask = "[SSH] #{self} (#{cmd})"
80
+ masked_string = Util.mask_values(string_to_mask, %w{password ssh_http_proxy_password})
81
+ logger.debug(masked_string)
79
82
  exit_code = exec_with_exit(cmd)
80
83
 
81
84
  if exit_code != 0
@@ -137,7 +140,9 @@ module Kitchen
137
140
  def shutdown
138
141
  return if @session.nil?
139
142
 
140
- logger.debug("[SSH] closing connection to #{self}")
143
+ string_to_mask = "[SSH] closing connection to #{self}"
144
+ masked_string = Util.mask_values(string_to_mask, %w{password ssh_http_proxy_password})
145
+ logger.debug(masked_string)
141
146
  session.shutdown!
142
147
  ensure
143
148
  @session = nil
@@ -212,7 +217,9 @@ module Kitchen
212
217
  retries = options[:ssh_retries] || 3
213
218
 
214
219
  begin
215
- logger.debug("[SSH] opening connection to #{self}")
220
+ string_to_mask = "[SSH] opening connection to #{self}"
221
+ masked_string = Util.mask_values(string_to_mask, %w{password ssh_http_proxy_password})
222
+ logger.debug(masked_string)
216
223
  Net::SSH.start(hostname, username, options)
217
224
  rescue *rescue_exceptions => e
218
225
  retries -= 1
data/lib/kitchen/suite.rb CHANGED
@@ -15,6 +15,8 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
+ require_relative "platform_filter"
19
+
18
20
  module Kitchen
19
21
  # A logical configuration representing a test case or fixture that will be
20
22
  # executed on a platform.
@@ -41,8 +43,8 @@ module Kitchen
41
43
  @name = options.fetch(:name) do
42
44
  raise ClientError, "Suite#new requires option :name"
43
45
  end
44
- @excludes = options.fetch(:excludes, [])
45
- @includes = options.fetch(:includes, [])
46
+ @excludes = PlatformFilter.convert(options.fetch(:excludes, []))
47
+ @includes = PlatformFilter.convert(options.fetch(:includes, []))
46
48
  end
47
49
  end
48
50
  end
@@ -128,8 +128,8 @@ module Kitchen
128
128
  tries += 1
129
129
  debug("Attempting to execute command - try #{tries} of #{max_retries}.")
130
130
  execute(command)
131
- rescue Kitchen::Transport::TransportFailed => e
132
- if retry?(tries, max_retries, retryable_exit_codes, e.exit_code)
131
+ rescue Exception => e
132
+ if retry?(tries, max_retries, retryable_exit_codes, e)
133
133
  close
134
134
  sleep wait_time
135
135
  retry
@@ -139,10 +139,14 @@ module Kitchen
139
139
  end
140
140
  end
141
141
 
142
- def retry?(current_try, max_retries, retryable_exit_codes, exit_code)
143
- current_try <= max_retries &&
144
- !retryable_exit_codes.nil? &&
145
- retryable_exit_codes.flatten.include?(exit_code)
142
+ def retry?(current_try, max_retries, retryable_exit_codes, exception)
143
+ if exception.is_a?(Kitchen::Transport::TransportFailed)
144
+ return current_try <= max_retries &&
145
+ !retryable_exit_codes.nil? &&
146
+ retryable_exit_codes.flatten.include?(exception.exit_code)
147
+ end
148
+
149
+ false
146
150
  end
147
151
 
148
152
  # Builds a LoginCommand which can be used to open an interactive