test-kitchen 1.7.0 → 1.7.1.dev

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.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +8 -8
  3. data/.gitattributes +3 -0
  4. data/.github/ISSUE_TEMPLATE.md +55 -55
  5. data/.gitignore +28 -28
  6. data/.kitchen.ci.yml +23 -23
  7. data/.kitchen.proxy.yml +27 -27
  8. data/.rubocop.yml +3 -3
  9. data/.travis.yml +70 -70
  10. data/.yardopts +3 -3
  11. data/Berksfile +3 -3
  12. data/CHANGELOG.md +1090 -1083
  13. data/CONTRIBUTING.md +14 -14
  14. data/Gemfile +19 -19
  15. data/Gemfile.proxy_tests +4 -4
  16. data/Guardfile +42 -42
  17. data/LICENSE +15 -15
  18. data/MAINTAINERS.md +23 -23
  19. data/README.md +135 -135
  20. data/Rakefile +61 -61
  21. data/appveyor.yml +44 -44
  22. data/features/kitchen_action_commands.feature +164 -164
  23. data/features/kitchen_command.feature +16 -16
  24. data/features/kitchen_console_command.feature +34 -34
  25. data/features/kitchen_defaults.feature +38 -38
  26. data/features/kitchen_diagnose_command.feature +96 -96
  27. data/features/kitchen_driver_create_command.feature +64 -64
  28. data/features/kitchen_driver_discover_command.feature +25 -25
  29. data/features/kitchen_help_command.feature +16 -16
  30. data/features/kitchen_init_command.feature +274 -274
  31. data/features/kitchen_list_command.feature +104 -104
  32. data/features/kitchen_login_command.feature +62 -62
  33. data/features/kitchen_sink_command.feature +30 -30
  34. data/features/kitchen_test_command.feature +88 -88
  35. data/features/step_definitions/gem_steps.rb +36 -36
  36. data/features/step_definitions/git_steps.rb +5 -5
  37. data/features/step_definitions/output_steps.rb +5 -5
  38. data/features/support/env.rb +75 -75
  39. data/lib/kitchen.rb +150 -150
  40. data/lib/kitchen/base64_stream.rb +55 -55
  41. data/lib/kitchen/cli.rb +419 -419
  42. data/lib/kitchen/collection.rb +55 -55
  43. data/lib/kitchen/color.rb +65 -65
  44. data/lib/kitchen/command.rb +185 -185
  45. data/lib/kitchen/command/action.rb +45 -45
  46. data/lib/kitchen/command/console.rb +58 -58
  47. data/lib/kitchen/command/diagnose.rb +92 -92
  48. data/lib/kitchen/command/driver_discover.rb +105 -105
  49. data/lib/kitchen/command/exec.rb +41 -41
  50. data/lib/kitchen/command/list.rb +119 -119
  51. data/lib/kitchen/command/login.rb +43 -43
  52. data/lib/kitchen/command/sink.rb +54 -54
  53. data/lib/kitchen/command/test.rb +51 -51
  54. data/lib/kitchen/config.rb +322 -322
  55. data/lib/kitchen/configurable.rb +529 -529
  56. data/lib/kitchen/data_munger.rb +959 -959
  57. data/lib/kitchen/diagnostic.rb +141 -141
  58. data/lib/kitchen/driver.rb +56 -56
  59. data/lib/kitchen/driver/base.rb +134 -134
  60. data/lib/kitchen/driver/dummy.rb +108 -108
  61. data/lib/kitchen/driver/proxy.rb +72 -72
  62. data/lib/kitchen/driver/ssh_base.rb +357 -357
  63. data/lib/kitchen/errors.rb +229 -229
  64. data/lib/kitchen/generator/driver_create.rb +177 -177
  65. data/lib/kitchen/generator/init.rb +296 -296
  66. data/lib/kitchen/instance.rb +662 -662
  67. data/lib/kitchen/lazy_hash.rb +142 -142
  68. data/lib/kitchen/loader/yaml.rb +349 -349
  69. data/lib/kitchen/logger.rb +423 -423
  70. data/lib/kitchen/logging.rb +56 -56
  71. data/lib/kitchen/login_command.rb +52 -52
  72. data/lib/kitchen/metadata_chopper.rb +52 -52
  73. data/lib/kitchen/platform.rb +67 -67
  74. data/lib/kitchen/provisioner.rb +54 -54
  75. data/lib/kitchen/provisioner/base.rb +236 -236
  76. data/lib/kitchen/provisioner/chef/berkshelf.rb +114 -114
  77. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -322
  78. data/lib/kitchen/provisioner/chef/librarian.rb +112 -112
  79. data/lib/kitchen/provisioner/chef_apply.rb +124 -124
  80. data/lib/kitchen/provisioner/chef_base.rb +341 -341
  81. data/lib/kitchen/provisioner/chef_solo.rb +88 -88
  82. data/lib/kitchen/provisioner/chef_zero.rb +245 -245
  83. data/lib/kitchen/provisioner/dummy.rb +79 -79
  84. data/lib/kitchen/provisioner/shell.rb +138 -138
  85. data/lib/kitchen/rake_tasks.rb +63 -63
  86. data/lib/kitchen/shell_out.rb +93 -93
  87. data/lib/kitchen/ssh.rb +276 -276
  88. data/lib/kitchen/state_file.rb +120 -120
  89. data/lib/kitchen/suite.rb +51 -51
  90. data/lib/kitchen/thor_tasks.rb +66 -66
  91. data/lib/kitchen/transport.rb +54 -54
  92. data/lib/kitchen/transport/base.rb +176 -176
  93. data/lib/kitchen/transport/dummy.rb +79 -79
  94. data/lib/kitchen/transport/ssh.rb +364 -364
  95. data/lib/kitchen/transport/winrm.rb +486 -486
  96. data/lib/kitchen/util.rb +147 -147
  97. data/lib/kitchen/verifier.rb +55 -55
  98. data/lib/kitchen/verifier/base.rb +235 -235
  99. data/lib/kitchen/verifier/busser.rb +277 -277
  100. data/lib/kitchen/verifier/dummy.rb +79 -79
  101. data/lib/kitchen/verifier/shell.rb +101 -101
  102. data/lib/kitchen/version.rb +21 -21
  103. data/lib/vendor/hash_recursive_merge.rb +82 -82
  104. data/spec/kitchen/base64_stream_spec.rb +77 -77
  105. data/spec/kitchen/cli_spec.rb +56 -56
  106. data/spec/kitchen/collection_spec.rb +80 -80
  107. data/spec/kitchen/color_spec.rb +54 -54
  108. data/spec/kitchen/config_spec.rb +408 -408
  109. data/spec/kitchen/configurable_spec.rb +1095 -1095
  110. data/spec/kitchen/data_munger_spec.rb +2694 -2694
  111. data/spec/kitchen/diagnostic_spec.rb +129 -129
  112. data/spec/kitchen/driver/base_spec.rb +121 -121
  113. data/spec/kitchen/driver/dummy_spec.rb +199 -199
  114. data/spec/kitchen/driver/proxy_spec.rb +138 -138
  115. data/spec/kitchen/driver/ssh_base_spec.rb +1115 -1115
  116. data/spec/kitchen/driver_spec.rb +112 -112
  117. data/spec/kitchen/errors_spec.rb +309 -309
  118. data/spec/kitchen/instance_spec.rb +1419 -1419
  119. data/spec/kitchen/lazy_hash_spec.rb +117 -117
  120. data/spec/kitchen/loader/yaml_spec.rb +774 -774
  121. data/spec/kitchen/logger_spec.rb +429 -429
  122. data/spec/kitchen/logging_spec.rb +59 -59
  123. data/spec/kitchen/login_command_spec.rb +68 -68
  124. data/spec/kitchen/metadata_chopper_spec.rb +82 -82
  125. data/spec/kitchen/platform_spec.rb +89 -89
  126. data/spec/kitchen/provisioner/base_spec.rb +386 -386
  127. data/spec/kitchen/provisioner/chef_apply_spec.rb +136 -136
  128. data/spec/kitchen/provisioner/chef_base_spec.rb +1161 -1161
  129. data/spec/kitchen/provisioner/chef_solo_spec.rb +557 -557
  130. data/spec/kitchen/provisioner/chef_zero_spec.rb +1001 -1001
  131. data/spec/kitchen/provisioner/dummy_spec.rb +99 -99
  132. data/spec/kitchen/provisioner/shell_spec.rb +566 -566
  133. data/spec/kitchen/provisioner_spec.rb +107 -107
  134. data/spec/kitchen/shell_out_spec.rb +150 -150
  135. data/spec/kitchen/ssh_spec.rb +693 -693
  136. data/spec/kitchen/state_file_spec.rb +129 -129
  137. data/spec/kitchen/suite_spec.rb +62 -62
  138. data/spec/kitchen/transport/base_spec.rb +89 -89
  139. data/spec/kitchen/transport/ssh_spec.rb +1255 -1255
  140. data/spec/kitchen/transport/winrm_spec.rb +1143 -1143
  141. data/spec/kitchen/transport_spec.rb +112 -112
  142. data/spec/kitchen/util_spec.rb +165 -165
  143. data/spec/kitchen/verifier/base_spec.rb +362 -362
  144. data/spec/kitchen/verifier/busser_spec.rb +610 -610
  145. data/spec/kitchen/verifier/dummy_spec.rb +99 -99
  146. data/spec/kitchen/verifier/shell_spec.rb +160 -160
  147. data/spec/kitchen/verifier_spec.rb +120 -120
  148. data/spec/kitchen_spec.rb +114 -114
  149. data/spec/spec_helper.rb +85 -85
  150. data/spec/support/powershell_max_size_spec.rb +40 -40
  151. data/support/busser_install_command.ps1 +14 -14
  152. data/support/busser_install_command.sh +14 -14
  153. data/support/chef-client-zero.rb +77 -77
  154. data/support/chef_base_init_command.ps1 +18 -18
  155. data/support/chef_base_init_command.sh +2 -2
  156. data/support/chef_base_install_command.ps1 +85 -85
  157. data/support/chef_base_install_command.sh +229 -229
  158. data/support/chef_zero_prepare_command_legacy.ps1 +9 -9
  159. data/support/chef_zero_prepare_command_legacy.sh +10 -10
  160. data/support/download_helpers.sh +109 -109
  161. data/support/dummy-validation.pem +27 -27
  162. data/templates/driver/CHANGELOG.md.erb +3 -3
  163. data/templates/driver/Gemfile.erb +3 -3
  164. data/templates/driver/README.md.erb +64 -64
  165. data/templates/driver/Rakefile.erb +21 -21
  166. data/templates/driver/driver.rb.erb +23 -23
  167. data/templates/driver/gemspec.erb +29 -29
  168. data/templates/driver/gitignore.erb +17 -17
  169. data/templates/driver/license_apachev2.erb +15 -15
  170. data/templates/driver/license_lgplv3.erb +16 -16
  171. data/templates/driver/license_mit.erb +22 -22
  172. data/templates/driver/license_reserved.erb +5 -5
  173. data/templates/driver/tailor.erb +4 -4
  174. data/templates/driver/travis.yml.erb +11 -11
  175. data/templates/driver/version.rb.erb +12 -12
  176. data/templates/init/chefignore.erb +1 -1
  177. data/templates/init/kitchen.yml.erb +18 -18
  178. data/test-kitchen.gemspec +62 -62
  179. data/test/integration/default/default_spec.rb +3 -3
  180. data/testing_windows.md +37 -37
  181. metadata +5 -4
@@ -1,423 +1,423 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 2013, Fletcher Nichol
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
-
19
- require "fileutils"
20
- require "logger"
21
-
22
- module Kitchen
23
-
24
- # Logging implementation for Kitchen. By default the console/stdout output
25
- # will be displayed differently than the file log output. Therefor, this
26
- # class wraps multiple loggers that conform to the stdlib `Logger` class
27
- # behavior.
28
- #
29
- # @author Fletcher Nichol <fnichol@nichol.ca>
30
- class Logger
31
-
32
- include ::Logger::Severity
33
-
34
- # @return [IO] the log device
35
- attr_reader :logdev
36
-
37
- # @return [Boolean] whether logger is configured for
38
- # overwriting
39
- attr_reader :log_overwrite
40
-
41
- # Constructs a new logger.
42
- #
43
- # @param options [Hash] configuration for a new logger
44
- # @option options [Symbol] :color color to use when when outputting
45
- # messages
46
- # @option options [Integer] :level the logging severity threshold
47
- # (default: `Kitchen::DEFAULT_LOG_LEVEL`)
48
- # @option options [Boolean] whether to overwrite the log
49
- # when Test Kitchen runs. Only applies if the :logdev is a String.
50
- # (default: `Kitchen::DEFAULT_LOG_OVERWRITE`)
51
- # @option options [String,IO] :logdev filepath String or IO object to be
52
- # used for logging (default: `nil`)
53
- # @option options [String] :progname program name to include in log
54
- # messages (default: `"Kitchen"`)
55
- # @option options [IO] :stdout a standard out IO object to use
56
- # (default: `$stdout`)
57
- def initialize(options = {})
58
- color = options[:color]
59
- @log_overwrite = if options[:log_overwrite].nil?
60
- default_log_overwrite
61
- else
62
- options[:log_overwrite]
63
- end
64
-
65
- @logdev = logdev_logger(options[:logdev], log_overwrite) if options[:logdev]
66
-
67
- populate_loggers(color, options)
68
-
69
- # These setters cannot be called until @loggers are populated because
70
- # they are delegated
71
- self.progname = options[:progname] || "Kitchen"
72
- self.level = options[:level] || default_log_level
73
- end
74
-
75
- # Pulled out for Rubocop complexity issues
76
- #
77
- # @api private
78
- def populate_loggers(color, options)
79
- @loggers = []
80
- @loggers << logdev unless logdev.nil?
81
- @loggers << stdout_logger(options[:stdout], color) if options[:stdout]
82
- @loggers << stdout_logger($stdout, color) if @loggers.empty?
83
- end
84
- private :populate_loggers
85
-
86
- class << self
87
-
88
- private
89
-
90
- # @api private
91
- # @!macro delegate_to_first_logger
92
- # @method $1()
93
- def delegate_to_first_logger(meth)
94
- define_method(meth) { |*args| @loggers.first.public_send(meth, *args) }
95
- end
96
-
97
- # @api private
98
- # @!macro delegate_to_all_loggers
99
- # @method $1()
100
- def delegate_to_all_loggers(meth)
101
- define_method(meth) do |*args|
102
- result = nil
103
- @loggers.each { |l| result = l.public_send(meth, *args) }
104
- result
105
- end
106
- end
107
- end
108
-
109
- # @return [Integer] the logging severity threshold
110
- # @see http://is.gd/Okuy5p
111
- delegate_to_first_logger :level
112
-
113
- # Sets the logging severity threshold.
114
- #
115
- # @param level [Integer] the logging severity threshold
116
- # @see http://is.gd/H1VBFH
117
- delegate_to_all_loggers :level=
118
-
119
- # @return [String] program name to include in log messages
120
- # @see http://is.gd/5uHGK0
121
- delegate_to_first_logger :progname
122
-
123
- # Sets the program name to include in log messages.
124
- #
125
- # @param progname [String] the program name to include in log messages
126
- # @see http://is.gd/f2U5Xj
127
- delegate_to_all_loggers :progname=
128
-
129
- # @return [String] the date format being used
130
- # @see http://is.gd/btmFWJ
131
- delegate_to_first_logger :datetime_format
132
-
133
- # Sets the date format being used.
134
- #
135
- # @param format [String] the date format
136
- # @see http://is.gd/M36ml8
137
- delegate_to_all_loggers :datetime_format=
138
-
139
- # Log a message if the given severity is high enough.
140
- #
141
- # @see http://is.gd/5opBW0
142
- delegate_to_all_loggers :add
143
-
144
- # Dump one or more messages to info.
145
- #
146
- # @param message [#to_s] the message to log
147
- # @see http://is.gd/BCp5KV
148
- delegate_to_all_loggers :<<
149
-
150
- # Log a message with severity of banner (high level).
151
- #
152
- # @param message_or_progname [#to_s] the message to log. In the block
153
- # form, this is the progname to use in the log message.
154
- # @yield evaluates to the message to log. This is not evaluated unless the
155
- # logger's level is sufficient to log the message. This allows you to
156
- # create potentially expensive logging messages that are only called when
157
- # the logger is configured to show them.
158
- # @return [nil,true] when the given severity is not high enough (for this
159
- # particular logger), log no message, and return true
160
- # @see http://is.gd/pYUCYU
161
- delegate_to_all_loggers :banner
162
-
163
- # Log a message with severity of debug.
164
- #
165
- # @param message_or_progname [#to_s] the message to log. In the block
166
- # form, this is the progname to use in the log message.
167
- # @yield evaluates to the message to log. This is not evaluated unless the
168
- # logger's level is sufficient to log the message. This allows you to
169
- # create potentially expensive logging messages that are only called when
170
- # the logger is configured to show them.
171
- # @return [nil,true] when the given severity is not high enough (for this
172
- # particular logger), log no message, and return true
173
- # @see http://is.gd/Re97Zp
174
- delegate_to_all_loggers :debug
175
-
176
- # @return [true,false] whether or not the current severity level
177
- # allows for the printing of debug messages
178
- # @see http://is.gd/Iq08xB
179
- delegate_to_first_logger :debug?
180
-
181
- # Log a message with severity of info.
182
- #
183
- # @param message_or_progname [#to_s] the message to log. In the block
184
- # form, this is the progname to use in the log message.
185
- # @yield evaluates to the message to log. This is not evaluated unless the
186
- # logger's level is sufficient to log the message. This allows you to
187
- # create potentially expensive logging messages that are only called when
188
- # the logger is configured to show them.
189
- # @return [nil,true] when the given severity is not high enough (for this
190
- # particular logger), log no message, and return true
191
- # @see http://is.gd/pYUCYU
192
- delegate_to_all_loggers :info
193
-
194
- # @return [true,false] whether or not the current severity level
195
- # allows for the printing of info messages
196
- # @see http://is.gd/lBtJkT
197
- delegate_to_first_logger :info?
198
-
199
- # Log a message with severity of error.
200
- #
201
- # @param message_or_progname [#to_s] the message to log. In the block
202
- # form, this is the progname to use in the log message.
203
- # @yield evaluates to the message to log. This is not evaluated unless the
204
- # logger's level is sufficient to log the message. This allows you to
205
- # create potentially expensive logging messages that are only called when
206
- # the logger is configured to show them.
207
- # @return [nil,true] when the given severity is not high enough (for this
208
- # particular logger), log no message, and return true
209
- # @see http://is.gd/mLwYMl
210
- delegate_to_all_loggers :error
211
-
212
- # @return [true,false] whether or not the current severity level
213
- # allows for the printing of error messages
214
- # @see http://is.gd/QY19JL
215
- delegate_to_first_logger :error?
216
-
217
- # Log a message with severity of warn.
218
- #
219
- # @param message_or_progname [#to_s] the message to log. In the block
220
- # form, this is the progname to use in the log message.
221
- # @yield evaluates to the message to log. This is not evaluated unless the
222
- # logger's level is sufficient to log the message. This allows you to
223
- # create potentially expensive logging messages that are only called when
224
- # the logger is configured to show them.
225
- # @return [nil,true] when the given severity is not high enough (for this
226
- # particular logger), log no message, and return true
227
- # @see http://is.gd/PX9AIS
228
- delegate_to_all_loggers :warn
229
-
230
- # @return [true,false] whether or not the current severity level
231
- # allows for the printing of warn messages
232
- # @see http://is.gd/Gdr4lD
233
- delegate_to_first_logger :warn?
234
-
235
- # Log a message with severity of fatal.
236
- #
237
- # @param message_or_progname [#to_s] the message to log. In the block
238
- # form, this is the progname to use in the log message.
239
- # @yield evaluates to the message to log. This is not evaluated unless the
240
- # logger's level is sufficient to log the message. This allows you to
241
- # create potentially expensive logging messages that are only called when
242
- # the logger is configured to show them.
243
- # @return [nil,true] when the given severity is not high enough (for this
244
- # particular logger), log no message, and return true
245
- # @see http://is.gd/5ElFPK
246
- delegate_to_all_loggers :fatal
247
-
248
- # @return [true,false] whether or not the current severity level
249
- # allows for the printing of fatal messages
250
- # @see http://is.gd/7PgwRl
251
- delegate_to_first_logger :fatal?
252
-
253
- # Log a message with severity of unknown.
254
- #
255
- # @param message_or_progname [#to_s] the message to log. In the block
256
- # form, this is the progname to use in the log message.
257
- # @yield evaluates to the message to log. This is not evaluated unless the
258
- # logger's level is sufficient to log the message. This allows you to
259
- # create potentially expensive logging messages that are only called when
260
- # the logger is configured to show them.
261
- # @return [nil,true] when the given severity is not high enough (for this
262
- # particular logger), log no message, and return true
263
- # @see http://is.gd/Y4hqpf
264
- delegate_to_all_loggers :unknown
265
-
266
- # Close the logging devices.
267
- #
268
- # @see http://is.gd/b13cVn
269
- delegate_to_all_loggers :close
270
-
271
- private
272
-
273
- # @return [Integer] the default logger level
274
- # @api private
275
- def default_log_level
276
- Util.to_logger_level(Kitchen::DEFAULT_LOG_LEVEL)
277
- end
278
-
279
- # @return [Boolean] whether to overwrite logs by default
280
- # @api private
281
- def default_log_overwrite
282
- Kitchen::DEFAULT_LOG_OVERWRITE
283
- end
284
-
285
- # Construct a new standard out logger.
286
- #
287
- # @param stdout [IO] the IO object that represents stdout (or similar)
288
- # @param color [Symbol] color to use when outputing messages
289
- # @return [StdoutLogger] a new logger
290
- # @api private
291
- def stdout_logger(stdout, color)
292
- logger = StdoutLogger.new(stdout)
293
- if Kitchen.tty?
294
- logger.formatter = proc do |_severity, _datetime, _progname, msg|
295
- Color.colorize("#{msg}", color).concat("\n")
296
- end
297
- else
298
- logger.formatter = proc do |_severity, _datetime, _progname, msg|
299
- msg.concat("\n")
300
- end
301
- end
302
- logger
303
- end
304
-
305
- # Construct a new logdev logger.
306
- #
307
- # @param filepath_or_logdev [String,IO] a filepath String or IO object
308
- # @param log_overwrite [Boolean] apply log overwriting
309
- # if filepath_or_logdev is a file path
310
- # @return [LogdevLogger] a new logger
311
- # @api private
312
- def logdev_logger(filepath_or_logdev, log_overwrite)
313
- LogdevLogger.new(resolve_logdev(filepath_or_logdev, log_overwrite))
314
- end
315
-
316
- # Return an IO object from a filepath String or the IO object itself.
317
- #
318
- # @param filepath_or_logdev [String,IO] a filepath String or IO object
319
- # @param log_overwrite [Boolean] apply log overwriting
320
- # if filepath_or_logdev is a file path
321
- # @return [IO] an IO object
322
- # @api private
323
- def resolve_logdev(filepath_or_logdev, log_overwrite)
324
- if filepath_or_logdev.is_a? String
325
- mode = log_overwrite ? "wb" : "ab"
326
- FileUtils.mkdir_p(File.dirname(filepath_or_logdev))
327
- file = File.open(File.expand_path(filepath_or_logdev), mode)
328
- file.sync = true
329
- file
330
- else
331
- filepath_or_logdev
332
- end
333
- end
334
-
335
- # Internal class which adds a #banner method call that displays the
336
- # message with a callout arrow.
337
- class LogdevLogger < ::Logger
338
-
339
- alias_method :super_info, :info
340
-
341
- # Dump one or more messages to info.
342
- #
343
- # @param msg [String] a message
344
- def <<(msg)
345
- @buffer ||= ""
346
- @buffer += msg
347
- while i = @buffer.index("\n")
348
- format_line(@buffer[0, i].chomp)
349
- @buffer[0, i + 1] = ""
350
- end
351
- end
352
-
353
- # Log a banner message.
354
- #
355
- # @param msg [String] a message
356
- def banner(msg = nil, &block)
357
- super_info("-----> #{msg}", &block)
358
- end
359
-
360
- private
361
-
362
- # Reformat a line if it already contains log formatting.
363
- #
364
- # @param line [String] a message line
365
- # @api private
366
- def format_line(line)
367
- case line
368
- when %r{^-----> } then banner(line.gsub(%r{^[ >-]{6} }, ""))
369
- when %r{^>>>>>> } then error(line.gsub(%r{^[ >-]{6} }, ""))
370
- when %r{^ } then info(line.gsub(%r{^[ >-]{6} }, ""))
371
- else info(line)
372
- end
373
- end
374
- end
375
-
376
- # Internal class which reformats logging methods for display as console
377
- # output.
378
- class StdoutLogger < LogdevLogger
379
-
380
- # Log a debug message
381
- #
382
- # @param msg [String] a message
383
- def debug(msg = nil, &block)
384
- super("D #{msg}", &block)
385
- end
386
-
387
- # Log an info message
388
- #
389
- # @param msg [String] a message
390
- def info(msg = nil, &block)
391
- super(" #{msg}", &block)
392
- end
393
-
394
- # Log a warn message
395
- #
396
- # @param msg [String] a message
397
- def warn(msg = nil, &block)
398
- super("$$$$$$ #{msg}", &block)
399
- end
400
-
401
- # Log an error message
402
- #
403
- # @param msg [String] a message
404
- def error(msg = nil, &block)
405
- super(">>>>>> #{msg}", &block)
406
- end
407
-
408
- # Log a fatal message
409
- #
410
- # @param msg [String] a message
411
- def fatal(msg = nil, &block)
412
- super("!!!!!! #{msg}", &block)
413
- end
414
-
415
- # Log an unknown message
416
- #
417
- # @param msg [String] a message
418
- def unknown(msg = nil, &block)
419
- super("?????? #{msg}", &block)
420
- end
421
- end
422
- end
423
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2013, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "fileutils"
20
+ require "logger"
21
+
22
+ module Kitchen
23
+
24
+ # Logging implementation for Kitchen. By default the console/stdout output
25
+ # will be displayed differently than the file log output. Therefor, this
26
+ # class wraps multiple loggers that conform to the stdlib `Logger` class
27
+ # behavior.
28
+ #
29
+ # @author Fletcher Nichol <fnichol@nichol.ca>
30
+ class Logger
31
+
32
+ include ::Logger::Severity
33
+
34
+ # @return [IO] the log device
35
+ attr_reader :logdev
36
+
37
+ # @return [Boolean] whether logger is configured for
38
+ # overwriting
39
+ attr_reader :log_overwrite
40
+
41
+ # Constructs a new logger.
42
+ #
43
+ # @param options [Hash] configuration for a new logger
44
+ # @option options [Symbol] :color color to use when when outputting
45
+ # messages
46
+ # @option options [Integer] :level the logging severity threshold
47
+ # (default: `Kitchen::DEFAULT_LOG_LEVEL`)
48
+ # @option options [Boolean] whether to overwrite the log
49
+ # when Test Kitchen runs. Only applies if the :logdev is a String.
50
+ # (default: `Kitchen::DEFAULT_LOG_OVERWRITE`)
51
+ # @option options [String,IO] :logdev filepath String or IO object to be
52
+ # used for logging (default: `nil`)
53
+ # @option options [String] :progname program name to include in log
54
+ # messages (default: `"Kitchen"`)
55
+ # @option options [IO] :stdout a standard out IO object to use
56
+ # (default: `$stdout`)
57
+ def initialize(options = {})
58
+ color = options[:color]
59
+ @log_overwrite = if options[:log_overwrite].nil?
60
+ default_log_overwrite
61
+ else
62
+ options[:log_overwrite]
63
+ end
64
+
65
+ @logdev = logdev_logger(options[:logdev], log_overwrite) if options[:logdev]
66
+
67
+ populate_loggers(color, options)
68
+
69
+ # These setters cannot be called until @loggers are populated because
70
+ # they are delegated
71
+ self.progname = options[:progname] || "Kitchen"
72
+ self.level = options[:level] || default_log_level
73
+ end
74
+
75
+ # Pulled out for Rubocop complexity issues
76
+ #
77
+ # @api private
78
+ def populate_loggers(color, options)
79
+ @loggers = []
80
+ @loggers << logdev unless logdev.nil?
81
+ @loggers << stdout_logger(options[:stdout], color) if options[:stdout]
82
+ @loggers << stdout_logger($stdout, color) if @loggers.empty?
83
+ end
84
+ private :populate_loggers
85
+
86
+ class << self
87
+
88
+ private
89
+
90
+ # @api private
91
+ # @!macro delegate_to_first_logger
92
+ # @method $1()
93
+ def delegate_to_first_logger(meth)
94
+ define_method(meth) { |*args| @loggers.first.public_send(meth, *args) }
95
+ end
96
+
97
+ # @api private
98
+ # @!macro delegate_to_all_loggers
99
+ # @method $1()
100
+ def delegate_to_all_loggers(meth)
101
+ define_method(meth) do |*args|
102
+ result = nil
103
+ @loggers.each { |l| result = l.public_send(meth, *args) }
104
+ result
105
+ end
106
+ end
107
+ end
108
+
109
+ # @return [Integer] the logging severity threshold
110
+ # @see http://is.gd/Okuy5p
111
+ delegate_to_first_logger :level
112
+
113
+ # Sets the logging severity threshold.
114
+ #
115
+ # @param level [Integer] the logging severity threshold
116
+ # @see http://is.gd/H1VBFH
117
+ delegate_to_all_loggers :level=
118
+
119
+ # @return [String] program name to include in log messages
120
+ # @see http://is.gd/5uHGK0
121
+ delegate_to_first_logger :progname
122
+
123
+ # Sets the program name to include in log messages.
124
+ #
125
+ # @param progname [String] the program name to include in log messages
126
+ # @see http://is.gd/f2U5Xj
127
+ delegate_to_all_loggers :progname=
128
+
129
+ # @return [String] the date format being used
130
+ # @see http://is.gd/btmFWJ
131
+ delegate_to_first_logger :datetime_format
132
+
133
+ # Sets the date format being used.
134
+ #
135
+ # @param format [String] the date format
136
+ # @see http://is.gd/M36ml8
137
+ delegate_to_all_loggers :datetime_format=
138
+
139
+ # Log a message if the given severity is high enough.
140
+ #
141
+ # @see http://is.gd/5opBW0
142
+ delegate_to_all_loggers :add
143
+
144
+ # Dump one or more messages to info.
145
+ #
146
+ # @param message [#to_s] the message to log
147
+ # @see http://is.gd/BCp5KV
148
+ delegate_to_all_loggers :<<
149
+
150
+ # Log a message with severity of banner (high level).
151
+ #
152
+ # @param message_or_progname [#to_s] the message to log. In the block
153
+ # form, this is the progname to use in the log message.
154
+ # @yield evaluates to the message to log. This is not evaluated unless the
155
+ # logger's level is sufficient to log the message. This allows you to
156
+ # create potentially expensive logging messages that are only called when
157
+ # the logger is configured to show them.
158
+ # @return [nil,true] when the given severity is not high enough (for this
159
+ # particular logger), log no message, and return true
160
+ # @see http://is.gd/pYUCYU
161
+ delegate_to_all_loggers :banner
162
+
163
+ # Log a message with severity of debug.
164
+ #
165
+ # @param message_or_progname [#to_s] the message to log. In the block
166
+ # form, this is the progname to use in the log message.
167
+ # @yield evaluates to the message to log. This is not evaluated unless the
168
+ # logger's level is sufficient to log the message. This allows you to
169
+ # create potentially expensive logging messages that are only called when
170
+ # the logger is configured to show them.
171
+ # @return [nil,true] when the given severity is not high enough (for this
172
+ # particular logger), log no message, and return true
173
+ # @see http://is.gd/Re97Zp
174
+ delegate_to_all_loggers :debug
175
+
176
+ # @return [true,false] whether or not the current severity level
177
+ # allows for the printing of debug messages
178
+ # @see http://is.gd/Iq08xB
179
+ delegate_to_first_logger :debug?
180
+
181
+ # Log a message with severity of info.
182
+ #
183
+ # @param message_or_progname [#to_s] the message to log. In the block
184
+ # form, this is the progname to use in the log message.
185
+ # @yield evaluates to the message to log. This is not evaluated unless the
186
+ # logger's level is sufficient to log the message. This allows you to
187
+ # create potentially expensive logging messages that are only called when
188
+ # the logger is configured to show them.
189
+ # @return [nil,true] when the given severity is not high enough (for this
190
+ # particular logger), log no message, and return true
191
+ # @see http://is.gd/pYUCYU
192
+ delegate_to_all_loggers :info
193
+
194
+ # @return [true,false] whether or not the current severity level
195
+ # allows for the printing of info messages
196
+ # @see http://is.gd/lBtJkT
197
+ delegate_to_first_logger :info?
198
+
199
+ # Log a message with severity of error.
200
+ #
201
+ # @param message_or_progname [#to_s] the message to log. In the block
202
+ # form, this is the progname to use in the log message.
203
+ # @yield evaluates to the message to log. This is not evaluated unless the
204
+ # logger's level is sufficient to log the message. This allows you to
205
+ # create potentially expensive logging messages that are only called when
206
+ # the logger is configured to show them.
207
+ # @return [nil,true] when the given severity is not high enough (for this
208
+ # particular logger), log no message, and return true
209
+ # @see http://is.gd/mLwYMl
210
+ delegate_to_all_loggers :error
211
+
212
+ # @return [true,false] whether or not the current severity level
213
+ # allows for the printing of error messages
214
+ # @see http://is.gd/QY19JL
215
+ delegate_to_first_logger :error?
216
+
217
+ # Log a message with severity of warn.
218
+ #
219
+ # @param message_or_progname [#to_s] the message to log. In the block
220
+ # form, this is the progname to use in the log message.
221
+ # @yield evaluates to the message to log. This is not evaluated unless the
222
+ # logger's level is sufficient to log the message. This allows you to
223
+ # create potentially expensive logging messages that are only called when
224
+ # the logger is configured to show them.
225
+ # @return [nil,true] when the given severity is not high enough (for this
226
+ # particular logger), log no message, and return true
227
+ # @see http://is.gd/PX9AIS
228
+ delegate_to_all_loggers :warn
229
+
230
+ # @return [true,false] whether or not the current severity level
231
+ # allows for the printing of warn messages
232
+ # @see http://is.gd/Gdr4lD
233
+ delegate_to_first_logger :warn?
234
+
235
+ # Log a message with severity of fatal.
236
+ #
237
+ # @param message_or_progname [#to_s] the message to log. In the block
238
+ # form, this is the progname to use in the log message.
239
+ # @yield evaluates to the message to log. This is not evaluated unless the
240
+ # logger's level is sufficient to log the message. This allows you to
241
+ # create potentially expensive logging messages that are only called when
242
+ # the logger is configured to show them.
243
+ # @return [nil,true] when the given severity is not high enough (for this
244
+ # particular logger), log no message, and return true
245
+ # @see http://is.gd/5ElFPK
246
+ delegate_to_all_loggers :fatal
247
+
248
+ # @return [true,false] whether or not the current severity level
249
+ # allows for the printing of fatal messages
250
+ # @see http://is.gd/7PgwRl
251
+ delegate_to_first_logger :fatal?
252
+
253
+ # Log a message with severity of unknown.
254
+ #
255
+ # @param message_or_progname [#to_s] the message to log. In the block
256
+ # form, this is the progname to use in the log message.
257
+ # @yield evaluates to the message to log. This is not evaluated unless the
258
+ # logger's level is sufficient to log the message. This allows you to
259
+ # create potentially expensive logging messages that are only called when
260
+ # the logger is configured to show them.
261
+ # @return [nil,true] when the given severity is not high enough (for this
262
+ # particular logger), log no message, and return true
263
+ # @see http://is.gd/Y4hqpf
264
+ delegate_to_all_loggers :unknown
265
+
266
+ # Close the logging devices.
267
+ #
268
+ # @see http://is.gd/b13cVn
269
+ delegate_to_all_loggers :close
270
+
271
+ private
272
+
273
+ # @return [Integer] the default logger level
274
+ # @api private
275
+ def default_log_level
276
+ Util.to_logger_level(Kitchen::DEFAULT_LOG_LEVEL)
277
+ end
278
+
279
+ # @return [Boolean] whether to overwrite logs by default
280
+ # @api private
281
+ def default_log_overwrite
282
+ Kitchen::DEFAULT_LOG_OVERWRITE
283
+ end
284
+
285
+ # Construct a new standard out logger.
286
+ #
287
+ # @param stdout [IO] the IO object that represents stdout (or similar)
288
+ # @param color [Symbol] color to use when outputing messages
289
+ # @return [StdoutLogger] a new logger
290
+ # @api private
291
+ def stdout_logger(stdout, color)
292
+ logger = StdoutLogger.new(stdout)
293
+ if Kitchen.tty?
294
+ logger.formatter = proc do |_severity, _datetime, _progname, msg|
295
+ Color.colorize("#{msg}", color).concat("\n")
296
+ end
297
+ else
298
+ logger.formatter = proc do |_severity, _datetime, _progname, msg|
299
+ msg.concat("\n")
300
+ end
301
+ end
302
+ logger
303
+ end
304
+
305
+ # Construct a new logdev logger.
306
+ #
307
+ # @param filepath_or_logdev [String,IO] a filepath String or IO object
308
+ # @param log_overwrite [Boolean] apply log overwriting
309
+ # if filepath_or_logdev is a file path
310
+ # @return [LogdevLogger] a new logger
311
+ # @api private
312
+ def logdev_logger(filepath_or_logdev, log_overwrite)
313
+ LogdevLogger.new(resolve_logdev(filepath_or_logdev, log_overwrite))
314
+ end
315
+
316
+ # Return an IO object from a filepath String or the IO object itself.
317
+ #
318
+ # @param filepath_or_logdev [String,IO] a filepath String or IO object
319
+ # @param log_overwrite [Boolean] apply log overwriting
320
+ # if filepath_or_logdev is a file path
321
+ # @return [IO] an IO object
322
+ # @api private
323
+ def resolve_logdev(filepath_or_logdev, log_overwrite)
324
+ if filepath_or_logdev.is_a? String
325
+ mode = log_overwrite ? "wb" : "ab"
326
+ FileUtils.mkdir_p(File.dirname(filepath_or_logdev))
327
+ file = File.open(File.expand_path(filepath_or_logdev), mode)
328
+ file.sync = true
329
+ file
330
+ else
331
+ filepath_or_logdev
332
+ end
333
+ end
334
+
335
+ # Internal class which adds a #banner method call that displays the
336
+ # message with a callout arrow.
337
+ class LogdevLogger < ::Logger
338
+
339
+ alias_method :super_info, :info
340
+
341
+ # Dump one or more messages to info.
342
+ #
343
+ # @param msg [String] a message
344
+ def <<(msg)
345
+ @buffer ||= ""
346
+ @buffer += msg
347
+ while i = @buffer.index("\n")
348
+ format_line(@buffer[0, i].chomp)
349
+ @buffer[0, i + 1] = ""
350
+ end
351
+ end
352
+
353
+ # Log a banner message.
354
+ #
355
+ # @param msg [String] a message
356
+ def banner(msg = nil, &block)
357
+ super_info("-----> #{msg}", &block)
358
+ end
359
+
360
+ private
361
+
362
+ # Reformat a line if it already contains log formatting.
363
+ #
364
+ # @param line [String] a message line
365
+ # @api private
366
+ def format_line(line)
367
+ case line
368
+ when %r{^-----> } then banner(line.gsub(%r{^[ >-]{6} }, ""))
369
+ when %r{^>>>>>> } then error(line.gsub(%r{^[ >-]{6} }, ""))
370
+ when %r{^ } then info(line.gsub(%r{^[ >-]{6} }, ""))
371
+ else info(line)
372
+ end
373
+ end
374
+ end
375
+
376
+ # Internal class which reformats logging methods for display as console
377
+ # output.
378
+ class StdoutLogger < LogdevLogger
379
+
380
+ # Log a debug message
381
+ #
382
+ # @param msg [String] a message
383
+ def debug(msg = nil, &block)
384
+ super("D #{msg}", &block)
385
+ end
386
+
387
+ # Log an info message
388
+ #
389
+ # @param msg [String] a message
390
+ def info(msg = nil, &block)
391
+ super(" #{msg}", &block)
392
+ end
393
+
394
+ # Log a warn message
395
+ #
396
+ # @param msg [String] a message
397
+ def warn(msg = nil, &block)
398
+ super("$$$$$$ #{msg}", &block)
399
+ end
400
+
401
+ # Log an error message
402
+ #
403
+ # @param msg [String] a message
404
+ def error(msg = nil, &block)
405
+ super(">>>>>> #{msg}", &block)
406
+ end
407
+
408
+ # Log a fatal message
409
+ #
410
+ # @param msg [String] a message
411
+ def fatal(msg = nil, &block)
412
+ super("!!!!!! #{msg}", &block)
413
+ end
414
+
415
+ # Log an unknown message
416
+ #
417
+ # @param msg [String] a message
418
+ def unknown(msg = nil, &block)
419
+ super("?????? #{msg}", &block)
420
+ end
421
+ end
422
+ end
423
+ end