test-kitchen 4.0.0 → 4.1.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: 80afb157c2779c990f0194d9711cf3a35d949a1916515fbaf722056306314a6d
4
- data.tar.gz: d06ecf45e9c7c2288a772c2202505f2d5cdd722b357fb7fa9cf0433e06eec725
3
+ metadata.gz: 37110df4f18f03d75c627d0095a7cfee87e12add45de4265ddbcb3afe5d207da
4
+ data.tar.gz: 94c3173ff1268904ec591d0de22f15635e1e1a453ce9f3167a4e10c26f6eed48
5
5
  SHA512:
6
- metadata.gz: 323e94d69dfadaa75a6214b504f47c7114227ee5ce98ae5dcdd64f9961945caf8212d9ef015a778ace055b6b63eb6fa5917dc072aa7f977a9da2a734ccfa66cd
7
- data.tar.gz: fcf0647d42a9482a9da0656ed68b90b6cc17eb8a14cd999a6316b3be4a2cc02796a3ef4a0ccf7ee8938a0cd92bb8910efb7243342f6677f63b624f8b9756cb64
6
+ metadata.gz: 30070f63b980cdf6ee9139232b013092c08ee838c6534b4af11736beeab7bf0aab1e3bba152665bbc07ac6a9723cee89e1c0cf6251dc4054a8c281c2866274d8
7
+ data.tar.gz: 1e1ab92e31f6b83b97df09df37e6e853be9f982b78aeabbf96045a1b0ee61fd8012704ad45fdcf2ff249fb489211e470ab5eed8a2db49209597fa05ae473a121
data/Gemfile CHANGED
@@ -7,7 +7,7 @@ group :test do
7
7
  gem "rb-readline"
8
8
  gem "aruba", ">= 0.11", "< 3.0"
9
9
  gem "countloc", "~> 0.4"
10
- gem "cucumber", ">= 9.2", "< 11"
10
+ gem "cucumber", ">= 9.2", "< 12"
11
11
  gem "fakefs", "~> 3.0"
12
12
  gem "maruku", "~> 0.7"
13
13
  gem "minitest", "~> 6.0", "< 6.1"
@@ -15,11 +15,13 @@ group :test do
15
15
  end
16
16
 
17
17
  group :integration do
18
- gem "chef-cli"
18
+ gem "cinc-auditor-bin", source: "https://rubygems.cinc.sh"
19
+ gem "kitchen-cinc"
20
+ gem "kitchen-cinc-auditor",
21
+ git: "https://github.com/test-kitchen/kitchen-cinc-auditor.git",
22
+ ref: "3d0b89eaa13f12da08a8761970e39c0f564c24c6"
19
23
  gem "kitchen-dokken"
20
24
  gem "kitchen-vagrant"
21
- gem "kitchen-inspec"
22
- gem "kitchen-omnibus-chef", ">= 1.0"
23
25
  end
24
26
 
25
27
  group :linting do
data/lib/kitchen/cli.rb CHANGED
@@ -110,13 +110,20 @@ module Kitchen
110
110
  aliases: "-j",
111
111
  type: :boolean,
112
112
  desc: "Print data as JSON"
113
+ method_option :live,
114
+ type: :boolean,
115
+ desc: "Include driver-reported live instance status"
116
+ method_option :probe,
117
+ type: :boolean,
118
+ desc: "Probe transport connectivity in addition to driver status"
113
119
  method_option :debug,
114
120
  aliases: "-d",
115
121
  type: :boolean,
116
122
  desc: "[Deprecated] Please use `kitchen diagnose'"
117
123
  log_options
118
124
  def list(*args)
119
- update_config!
125
+ log_overwrite = options[:log_overwrite].nil? ? false : options[:log_overwrite]
126
+ update_config!(log_overwrite:)
120
127
  perform("list", "list", args)
121
128
  end
122
129
  map status: :list
@@ -148,8 +155,7 @@ module Kitchen
148
155
  converge: "Change instance state to converge. " \
149
156
  "Use a provisioner to configure one or more instances",
150
157
  setup: "Change instance state to setup. " \
151
- "Prepare to run automated tests. " \
152
- "Install busser and related gems on one or more instances",
158
+ "Prepare one or more instances for verification",
153
159
  verify: "Change instance state to verify. " \
154
160
  "Run automated tests on one or more instances",
155
161
  destroy: "Change instance state to destroy. " \
@@ -257,6 +263,27 @@ module Kitchen
257
263
  perform("login", "login", args)
258
264
  end
259
265
 
266
+ desc "logs [INSTANCE|REGEXP|all]", "Show structured logs"
267
+ method_option :level,
268
+ desc: "Minimum log level to print (debug, info, warn, error, fatal)"
269
+ method_option :session_id,
270
+ desc: "Only print logs for the given instance session id"
271
+ method_option :all_sessions,
272
+ type: :boolean,
273
+ desc: "Print logs from all instance sessions; defaults to all instances"
274
+ method_option :format,
275
+ default: "text",
276
+ desc: "Output format (ndjson, text)"
277
+ method_option :follow,
278
+ aliases: "-f",
279
+ type: :boolean,
280
+ desc: "Follow the structured log file"
281
+ log_options
282
+ def logs(*args)
283
+ update_config!(log_overwrite: false)
284
+ perform("logs", "logs", args)
285
+ end
286
+
260
287
  desc "package INSTANCE|REGEXP", "package an instance"
261
288
  log_options
262
289
  def package(*args)
@@ -334,12 +361,10 @@ module Kitchen
334
361
  # Update and finalize options for logging, concurrency, and other concerns.
335
362
  #
336
363
  # @api private
337
- def update_config!
364
+ def update_config!(log_overwrite: options[:log_overwrite])
338
365
  @config.log_level = log_level if log_level
339
366
 
340
- unless options[:log_overwrite].nil?
341
- @config.log_overwrite = options[:log_overwrite]
342
- end
367
+ @config.log_overwrite = log_overwrite unless log_overwrite.nil?
343
368
  @config.colorize = options[:color] unless options[:color].nil?
344
369
 
345
370
  if options[:test_base_path]
@@ -352,7 +377,7 @@ module Kitchen
352
377
  # Now that we have required configs, lets create our file logger
353
378
  Kitchen.logger = Kitchen.default_file_logger(
354
379
  log_level,
355
- options[:log_overwrite]
380
+ log_overwrite
356
381
  )
357
382
 
358
383
  update_parallel!
@@ -57,7 +57,7 @@ module Kitchen
57
57
  # @return [Array<String>]
58
58
  # @api private
59
59
  def display_instance(instance)
60
- [
60
+ row = [
61
61
  color_pad(instance.name),
62
62
  color_pad(instance.driver.name),
63
63
  color_pad(instance.provisioner.name),
@@ -66,6 +66,8 @@ module Kitchen
66
66
  format_last_action(instance.last_action),
67
67
  format_last_error(instance.last_error),
68
68
  ]
69
+ row << format_live_status(instance_status(instance)) if status_requested?
70
+ row
69
71
  end
70
72
 
71
73
  # Format and color the given last action.
@@ -96,19 +98,39 @@ module Kitchen
96
98
  end
97
99
  end
98
100
 
101
+ # Format and color live status.
102
+ #
103
+ # @param status [Hash] normalized status data
104
+ # @return [String] formatted live status
105
+ # @api private
106
+ def format_live_status(status)
107
+ probe = status[:transport_probe]
108
+ if probe && probe[:reachable] == false
109
+ return colorize("#{status[:state]} (#{probe[:state]})", :red)
110
+ elsif probe && probe[:reachable] == true
111
+ return colorize("#{status[:state]} (#{probe[:state]})", :green)
112
+ end
113
+
114
+ case status[:live]
115
+ when true then colorize(status[:state], :green)
116
+ when false then colorize(status[:state], :red)
117
+ else colorize(status[:state], :white)
118
+ end
119
+ end
120
+
99
121
  # Constructs a list display table and output it to the screen.
100
122
  #
101
123
  # @param result [Array<Instance>] an array of instances
102
124
  # @api private
103
125
  def list_table(result)
104
- table = [
105
- [
106
- colorize("Instance", :green), colorize("Driver", :green),
107
- colorize("Provisioner", :green), colorize("Verifier", :green),
108
- colorize("Transport", :green), colorize("Last Action", :green),
109
- colorize("Last Error", :green)
110
- ],
126
+ headings = [
127
+ colorize("Instance", :green), colorize("Driver", :green),
128
+ colorize("Provisioner", :green), colorize("Verifier", :green),
129
+ colorize("Transport", :green), colorize("Last Action", :green),
130
+ colorize("Last Error", :green)
111
131
  ]
132
+ headings << colorize("Live Status", :green) if status_requested?
133
+ table = [headings]
112
134
  table += Array(result).map { |i| display_instance(i) }
113
135
  print_table(table)
114
136
  end
@@ -118,7 +140,7 @@ module Kitchen
118
140
  # @param result [Hash{Symbol => String}] hash of a single instance
119
141
  # @api private
120
142
  def to_hash(result)
121
- {
143
+ data = {
122
144
  instance: result.name,
123
145
  driver: result.driver.name,
124
146
  provisioner: result.provisioner.name,
@@ -127,6 +149,24 @@ module Kitchen
127
149
  last_action: result.last_action,
128
150
  last_error: result.last_error,
129
151
  }
152
+ if status_requested?
153
+ data.merge!(
154
+ log_path: result.log_path,
155
+ structured_log_path: result.structured_log_path,
156
+ state_path: result.state_path,
157
+ instance_session_id: result.current_session_id,
158
+ status: instance_status(result)
159
+ )
160
+ end
161
+ data
162
+ end
163
+
164
+ def status_requested?
165
+ options[:live] || options[:probe]
166
+ end
167
+
168
+ def instance_status(instance)
169
+ options[:probe] ? instance.status(probe: true) : instance.status
130
170
  end
131
171
 
132
172
  # Outputs a formatted display table.
@@ -0,0 +1,185 @@
1
+ #
2
+ # Licensed under the Apache License, Version 2.0 (the "License");
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # https://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an "AS IS" BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+
14
+ require_relative "../command"
15
+
16
+ require "json" unless defined?(JSON)
17
+
18
+ module Kitchen
19
+ module Command
20
+ # Command to print structured logs for one instance.
21
+ class Logs < Kitchen::Command::Base
22
+ LEVELS = %w{debug info warn error fatal unknown}.freeze
23
+
24
+ # Invoke the command.
25
+ def call
26
+ validate_format!
27
+ validate_level!
28
+ if options[:all_sessions]
29
+ emit_all_session_logs
30
+ return
31
+ end
32
+
33
+ target_instances = instances
34
+ validate_follow_target!(target_instances)
35
+
36
+ target_instances.each do |instance|
37
+ session_id = selected_session_id(instance)
38
+ die "No structured log file found at #{instance.structured_log_path}" unless
39
+ File.file?(instance.structured_log_path)
40
+
41
+ emit_file(instance.structured_log_path, session_id)
42
+ follow_file(instance.structured_log_path, session_id) if options[:follow]
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def emit_all_session_logs
49
+ log_paths = all_session_log_paths
50
+ validate_follow_target!(log_paths)
51
+
52
+ log_paths.each do |path|
53
+ emit_file(path, nil)
54
+ follow_file(path, nil) if options[:follow]
55
+ end
56
+ end
57
+
58
+ def all_session_log_paths
59
+ paths = structured_log_paths(args.first)
60
+ return paths unless paths.empty?
61
+
62
+ die "No structured log files found in #{Kitchen::DEFAULT_LOG_DIR}"
63
+ end
64
+
65
+ def structured_log_paths(arg)
66
+ paths = all_structured_log_paths
67
+ return paths if arg.nil? || arg == "all"
68
+
69
+ exact_path = File.join(Kitchen::DEFAULT_LOG_DIR, "#{arg}.ndjson")
70
+ return [exact_path] if File.file?(exact_path)
71
+
72
+ regexp = Regexp.new(arg)
73
+ paths.select { |path| File.basename(path, ".ndjson") =~ regexp }
74
+ rescue RegexpError => e
75
+ die "Invalid Ruby regular expression, " \
76
+ "you may need to single quote the argument. " \
77
+ "Please try again or consult https://rubular.com/ (#{e.message})"
78
+ end
79
+
80
+ def all_structured_log_paths
81
+ Dir[File.join(Kitchen::DEFAULT_LOG_DIR, "*.ndjson")]
82
+ .reject { |path| File.basename(path) == "kitchen.ndjson" }
83
+ .sort
84
+ end
85
+
86
+ def validate_follow_target!(targets)
87
+ return unless options[:follow] && targets.size > 1
88
+
89
+ die "Following multiple instance logs is not supported; choose one instance"
90
+ end
91
+
92
+ def instances
93
+ results = parse_subcommand(args.first)
94
+ if results.size > 1 && !options[:all_sessions]
95
+ die "Argument `#{args.first}' returned multiple results:\n" +
96
+ results.map { |i| " * #{i.name}" }.join("\n")
97
+ end
98
+ results
99
+ end
100
+
101
+ def selected_session_id(instance)
102
+ return if options[:all_sessions]
103
+ return options[:session_id] if options[:session_id]
104
+ return instance.current_session_id if instance.current_session_id
105
+
106
+ if File.file?(instance.structured_log_path)
107
+ session_id = latest_session_id(instance.structured_log_path)
108
+ return session_id if session_id
109
+ end
110
+
111
+ die "Instance #{instance.to_str} has no current session id; use --all-sessions"
112
+ end
113
+
114
+ def latest_session_id(path)
115
+ File.foreach(path).filter_map do |line|
116
+ JSON.parse(line)["instance_session_id"]
117
+ rescue JSON::ParserError
118
+ nil
119
+ end.last
120
+ end
121
+
122
+ def emit_file(path, session_id)
123
+ File.foreach(path) { |line| emit_line(line, session_id) }
124
+ end
125
+
126
+ def follow_file(path, session_id)
127
+ File.open(path, "r") do |file|
128
+ file.seek(0, IO::SEEK_END)
129
+ loop do
130
+ line = file.gets
131
+ if line
132
+ emit_line(line, session_id)
133
+ else
134
+ sleep 1
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ def emit_line(line, session_id)
141
+ event = JSON.parse(line)
142
+ return unless session_match?(event, session_id)
143
+ return unless level_match?(event)
144
+
145
+ puts format_event(event)
146
+ rescue JSON::ParserError
147
+ nil
148
+ end
149
+
150
+ def format_event(event)
151
+ case options[:format]
152
+ when "ndjson" then JSON.generate(event)
153
+ when nil, "text" then event["message"].to_s
154
+ end
155
+ end
156
+
157
+ def session_match?(event, session_id)
158
+ session_id.nil? || event["instance_session_id"] == session_id
159
+ end
160
+
161
+ def level_match?(event)
162
+ return true unless options[:level]
163
+
164
+ level_index(event["level"]) >= level_index(options[:level])
165
+ end
166
+
167
+ def level_index(level)
168
+ LEVELS.index(level.to_s) || LEVELS.index("unknown")
169
+ end
170
+
171
+ def validate_format!
172
+ return if [nil, "ndjson", "text"].include?(options[:format])
173
+
174
+ die "Invalid logs format `#{options[:format]}'; use ndjson or text"
175
+ end
176
+
177
+ def validate_level!
178
+ return unless options[:level]
179
+ return if LEVELS.include?(options[:level])
180
+
181
+ die "Invalid log level `#{options[:level]}'; use debug, info, warn, error, or fatal"
182
+ end
183
+ end
184
+ end
185
+ end
@@ -16,6 +16,7 @@
16
16
  # limitations under the License.
17
17
 
18
18
  require_relative "../command"
19
+ require_relative "../errors"
19
20
 
20
21
  require "benchmark" unless defined?(Benchmark)
21
22
 
@@ -30,7 +31,7 @@ module Kitchen
30
31
  # Invoke the command.
31
32
  def call
32
33
  unless %w{passing always never}.include?(options[:destroy])
33
- raise ArgumentError, "Destroy mode must be passing, always, or never."
34
+ raise UserError, "Destroy mode must be passing, always, or never."
34
35
  end
35
36
 
36
37
  banner "Starting Test Kitchen (v#{Kitchen::VERSION})"
@@ -15,6 +15,9 @@
15
15
  # See the License for the specific language governing permissions and
16
16
  # limitations under the License.
17
17
 
18
+ require_relative "errors"
19
+ require_relative "logging"
20
+
18
21
  module Kitchen
19
22
  module Command
20
23
  # Base class for CLI commands.
@@ -160,16 +163,22 @@ module Kitchen
160
163
 
161
164
  threads = []
162
165
  @action_errors = []
163
- concurrency.times do
164
- threads << Thread.new do
165
- while (instance = queue.pop)
166
- run_action_in_thread(action, instance, *args)
166
+ @action_errors_mutex = Mutex.new
167
+ previous_abort_on_exception = Thread.abort_on_exception
168
+ begin
169
+ Thread.abort_on_exception = true if options[:fail_fast]
170
+ concurrency.times do
171
+ threads << Thread.new do
172
+ while (instance = queue.pop)
173
+ run_action_in_thread(action, instance, *args)
174
+ end
167
175
  end
168
176
  end
177
+ threads.map(&:join)
178
+ report_errors
179
+ ensure
180
+ Thread.abort_on_exception = previous_abort_on_exception
169
181
  end
170
- Thread.abort_on_exception = true if options[:fail_fast]
171
- threads.map(&:join)
172
- report_errors
173
182
  end
174
183
 
175
184
  # private
@@ -194,14 +203,18 @@ module Kitchen
194
203
  def run_action_in_thread(action, instance, *args)
195
204
  instance.public_send(action, *args)
196
205
  rescue Kitchen::InstanceFailure => e
197
- @action_errors << e
206
+ record_action_error(e)
198
207
  rescue Kitchen::ActionFailed => e
199
208
  new_error = Kitchen::ActionFailed.new("#{e.message} on #{instance.name}")
200
209
  new_error.set_backtrace(e.backtrace)
201
- @action_errors << new_error
210
+ record_action_error(new_error)
202
211
  ensure
203
212
  instance.cleanup!
204
213
  end
214
+
215
+ def record_action_error(error)
216
+ @action_errors_mutex.synchronize { @action_errors << error }
217
+ end
205
218
  end
206
219
  end
207
220
  end
@@ -270,13 +270,21 @@ module Kitchen
270
270
  def new_instance_logger(suite, platform, index)
271
271
  name = instance_name(suite, platform)
272
272
  log_location = File.join(log_root, "#{name}.log").to_s
273
+ structured_log_location = File.join(log_root, "#{name}.ndjson").to_s
273
274
  Logger.new(
274
275
  stdout: STDOUT,
275
276
  color: Color::COLORS[index % Color::COLORS.size].to_sym,
276
277
  logdev: log_location,
278
+ structured_logdev: structured_log_location,
277
279
  level: Util.to_logger_level(log_level),
278
280
  log_overwrite:,
279
281
  progname: name,
282
+ metadata: {
283
+ kitchen_run_id: Kitchen.run_id,
284
+ instance: name,
285
+ suite: suite.name,
286
+ platform: platform.name,
287
+ },
280
288
  colorize: @colorize
281
289
  )
282
290
  end
@@ -38,13 +38,7 @@ module Kitchen
38
38
  def initialize(data, kitchen_config = {})
39
39
  @data = data
40
40
  @kitchen_config = kitchen_config
41
- convert_legacy_driver_format!
42
- convert_legacy_chef_paths_format!
43
- convert_legacy_require_chef_omnibus_format!
44
- convert_legacy_busser_format!
45
- convert_legacy_driver_http_proxy_format!
46
- move_chef_data_to_provisioner!
47
- convert_legacy_pre_create_command!
41
+ LegacyConfigNormalizer.new(self).normalize!
48
42
  end
49
43
 
50
44
  # Generate a new Hash of configuration data that can be used to construct
@@ -651,12 +645,53 @@ module Kitchen
651
645
  # @return [Hash] a new merged Hash
652
646
  # @api private
653
647
  def merged_data_for(key, suite, platform, default_key = :name)
654
- ddata = normalized_default_data(key, default_key, suite, platform)
655
- cdata = normalized_common_data(key, default_key)
656
- pdata = normalized_platform_data(key, default_key, platform)
657
- sdata = normalized_suite_data(key, default_key, suite)
648
+ MergeResolver.new(self).merged_data_for(key, suite, platform, default_key)
649
+ end
650
+
651
+ # Runs all legacy configuration conversions while keeping DataMunger as the
652
+ # public facade for destructive normalization.
653
+ class LegacyConfigNormalizer
654
+ STEPS = %i{
655
+ convert_legacy_driver_format!
656
+ convert_legacy_chef_paths_format!
657
+ convert_legacy_require_chef_omnibus_format!
658
+ convert_legacy_busser_format!
659
+ convert_legacy_driver_http_proxy_format!
660
+ move_chef_data_to_provisioner!
661
+ convert_legacy_pre_create_command!
662
+ }.freeze
663
+
664
+ def initialize(data_munger)
665
+ @data_munger = data_munger
666
+ end
667
+
668
+ def normalize!
669
+ STEPS.each { |step| data_munger.send(step) }
670
+ end
671
+
672
+ private
673
+
674
+ attr_reader :data_munger
675
+ end
676
+
677
+ # Resolves the default/common/platform/suite layers for one plugin key.
678
+ class MergeResolver
679
+ def initialize(data_munger)
680
+ @data_munger = data_munger
681
+ end
682
+
683
+ def merged_data_for(key, suite, platform, default_key)
684
+ ddata = data_munger.send(:normalized_default_data, key, default_key, suite, platform)
685
+ cdata = data_munger.send(:normalized_common_data, key, default_key)
686
+ pdata = data_munger.send(:normalized_platform_data, key, default_key, platform)
687
+ sdata = data_munger.send(:normalized_suite_data, key, default_key, suite)
688
+
689
+ ddata.rmerge(cdata.rmerge(pdata.rmerge(sdata)))
690
+ end
691
+
692
+ private
658
693
 
659
- ddata.rmerge(cdata.rmerge(pdata.rmerge(sdata)))
694
+ attr_reader :data_munger
660
695
  end
661
696
 
662
697
  # Destructively moves key Chef configuration key/value pairs from being
@@ -16,6 +16,7 @@ require_relative "../lazy_hash"
16
16
  require_relative "../logging"
17
17
  require_relative "../plugin_base"
18
18
  require_relative "../shell_out"
19
+ require "time" unless defined?(Time)
19
20
 
20
21
  module Kitchen
21
22
  module Driver
@@ -63,6 +64,24 @@ module Kitchen
63
64
  false
64
65
  end
65
66
 
67
+ # Reports whether the backing instance is known to be live.
68
+ #
69
+ # Drivers that can ask their provider should override this method.
70
+ # Existing drivers remain compatible by inheriting an unknown status.
71
+ #
72
+ # @param state [Hash] mutable instance and driver state
73
+ # @return [Hash] normalized status data
74
+ def status(state) # rubocop:disable Lint/UnusedMethodArgument
75
+ {
76
+ live: nil,
77
+ state: "unknown",
78
+ source: "driver",
79
+ resource_id: nil,
80
+ message: "#{self.class} does not support status checks",
81
+ checked_at: Time.now.utc.iso8601,
82
+ }
83
+ end
84
+
66
85
  # Sets the API version for this driver. If the driver does not set this
67
86
  # value, then `nil` will be used and reported.
68
87
  #
@@ -11,6 +11,7 @@
11
11
  # limitations under the License.
12
12
 
13
13
  require_relative "../../kitchen"
14
+ require "time" unless defined?(Time)
14
15
 
15
16
  module Kitchen
16
17
  module Driver
@@ -51,6 +52,32 @@ module Kitchen
51
52
  state.delete(:my_id)
52
53
  end
53
54
 
55
+ # Reports whether the dummy instance has a generated resource id.
56
+ #
57
+ # @param state [Hash] mutable instance and driver state
58
+ # @return [Hash] normalized status data
59
+ def status(state)
60
+ if state[:my_id]
61
+ {
62
+ live: true,
63
+ state: "running",
64
+ source: "driver",
65
+ resource_id: state[:my_id],
66
+ message: "Dummy instance exists",
67
+ checked_at: Time.now.utc.iso8601,
68
+ }
69
+ else
70
+ {
71
+ live: false,
72
+ state: "not_created",
73
+ source: "driver",
74
+ resource_id: nil,
75
+ message: "Dummy instance has no resource id",
76
+ checked_at: Time.now.utc.iso8601,
77
+ }
78
+ end
79
+ end
80
+
54
81
  private
55
82
 
56
83
  # Report what action is taking place, sleeping if so configured, and
@@ -206,6 +206,7 @@ module Kitchen
206
206
  Kitchen.logger.debug(line)
207
207
  else
208
208
  Kitchen.logger.logdev && Kitchen.logger.logdev.public_send(level, line)
209
+ Kitchen.logger.structured_logdev && Kitchen.logger.structured_logdev.public_send(level, line)
209
210
  end
210
211
  end
211
212
  end