test-kitchen 1.6.0 → 1.7.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.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +8 -7
  3. data/.github/ISSUE_TEMPLATE.md +56 -0
  4. data/.gitignore +28 -27
  5. data/.kitchen.ci.yml +23 -0
  6. data/.kitchen.proxy.yml +27 -0
  7. data/.rubocop.yml +3 -3
  8. data/.travis.yml +70 -53
  9. data/.yardopts +3 -3
  10. data/Berksfile +3 -0
  11. data/CHANGELOG.md +1083 -1051
  12. data/CONTRIBUTING.md +14 -14
  13. data/Gemfile +19 -14
  14. data/Gemfile.proxy_tests +4 -5
  15. data/Guardfile +42 -42
  16. data/LICENSE +15 -15
  17. data/MAINTAINERS.md +23 -24
  18. data/README.md +135 -135
  19. data/Rakefile +61 -76
  20. data/appveyor.yml +44 -34
  21. data/features/kitchen_action_commands.feature +164 -164
  22. data/features/kitchen_command.feature +16 -16
  23. data/features/kitchen_console_command.feature +34 -34
  24. data/features/kitchen_defaults.feature +38 -38
  25. data/features/kitchen_diagnose_command.feature +96 -96
  26. data/features/kitchen_driver_create_command.feature +64 -64
  27. data/features/kitchen_driver_discover_command.feature +25 -25
  28. data/features/kitchen_help_command.feature +16 -16
  29. data/features/kitchen_init_command.feature +274 -274
  30. data/features/kitchen_list_command.feature +104 -104
  31. data/features/kitchen_login_command.feature +62 -62
  32. data/features/kitchen_sink_command.feature +30 -30
  33. data/features/kitchen_test_command.feature +88 -88
  34. data/features/step_definitions/gem_steps.rb +36 -36
  35. data/features/step_definitions/git_steps.rb +5 -5
  36. data/features/step_definitions/output_steps.rb +5 -5
  37. data/features/support/env.rb +75 -75
  38. data/lib/kitchen.rb +150 -150
  39. data/lib/kitchen/base64_stream.rb +55 -55
  40. data/lib/kitchen/cli.rb +419 -419
  41. data/lib/kitchen/collection.rb +55 -55
  42. data/lib/kitchen/color.rb +65 -65
  43. data/lib/kitchen/command.rb +185 -185
  44. data/lib/kitchen/command/action.rb +45 -45
  45. data/lib/kitchen/command/console.rb +58 -58
  46. data/lib/kitchen/command/diagnose.rb +92 -92
  47. data/lib/kitchen/command/driver_discover.rb +105 -105
  48. data/lib/kitchen/command/exec.rb +41 -41
  49. data/lib/kitchen/command/list.rb +119 -119
  50. data/lib/kitchen/command/login.rb +43 -43
  51. data/lib/kitchen/command/sink.rb +54 -54
  52. data/lib/kitchen/command/test.rb +51 -51
  53. data/lib/kitchen/config.rb +322 -322
  54. data/lib/kitchen/configurable.rb +529 -529
  55. data/lib/kitchen/data_munger.rb +959 -960
  56. data/lib/kitchen/diagnostic.rb +141 -141
  57. data/lib/kitchen/driver.rb +56 -56
  58. data/lib/kitchen/driver/base.rb +134 -134
  59. data/lib/kitchen/driver/dummy.rb +108 -108
  60. data/lib/kitchen/driver/proxy.rb +72 -72
  61. data/lib/kitchen/driver/ssh_base.rb +357 -357
  62. data/lib/kitchen/errors.rb +229 -229
  63. data/lib/kitchen/generator/driver_create.rb +177 -177
  64. data/lib/kitchen/generator/init.rb +296 -296
  65. data/lib/kitchen/instance.rb +662 -662
  66. data/lib/kitchen/lazy_hash.rb +142 -142
  67. data/lib/kitchen/loader/yaml.rb +349 -349
  68. data/lib/kitchen/logger.rb +423 -423
  69. data/lib/kitchen/logging.rb +56 -56
  70. data/lib/kitchen/login_command.rb +52 -52
  71. data/lib/kitchen/metadata_chopper.rb +52 -52
  72. data/lib/kitchen/platform.rb +67 -67
  73. data/lib/kitchen/provisioner.rb +54 -54
  74. data/lib/kitchen/provisioner/base.rb +236 -236
  75. data/lib/kitchen/provisioner/chef/berkshelf.rb +114 -114
  76. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -322
  77. data/lib/kitchen/provisioner/chef/librarian.rb +112 -112
  78. data/lib/kitchen/provisioner/chef_apply.rb +124 -125
  79. data/lib/kitchen/provisioner/chef_base.rb +341 -294
  80. data/lib/kitchen/provisioner/chef_solo.rb +88 -89
  81. data/lib/kitchen/provisioner/chef_zero.rb +245 -245
  82. data/lib/kitchen/provisioner/dummy.rb +79 -79
  83. data/lib/kitchen/provisioner/shell.rb +138 -138
  84. data/lib/kitchen/rake_tasks.rb +63 -63
  85. data/lib/kitchen/shell_out.rb +93 -93
  86. data/lib/kitchen/ssh.rb +276 -276
  87. data/lib/kitchen/state_file.rb +120 -120
  88. data/lib/kitchen/suite.rb +51 -51
  89. data/lib/kitchen/thor_tasks.rb +66 -66
  90. data/lib/kitchen/transport.rb +54 -54
  91. data/lib/kitchen/transport/base.rb +176 -176
  92. data/lib/kitchen/transport/dummy.rb +79 -79
  93. data/lib/kitchen/transport/ssh.rb +364 -364
  94. data/lib/kitchen/transport/winrm.rb +486 -486
  95. data/lib/kitchen/util.rb +147 -147
  96. data/lib/kitchen/verifier.rb +55 -55
  97. data/lib/kitchen/verifier/base.rb +235 -235
  98. data/lib/kitchen/verifier/busser.rb +277 -277
  99. data/lib/kitchen/verifier/dummy.rb +79 -79
  100. data/lib/kitchen/verifier/shell.rb +101 -101
  101. data/lib/kitchen/version.rb +21 -21
  102. data/lib/vendor/hash_recursive_merge.rb +82 -82
  103. data/spec/kitchen/base64_stream_spec.rb +77 -77
  104. data/spec/kitchen/cli_spec.rb +56 -56
  105. data/spec/kitchen/collection_spec.rb +80 -80
  106. data/spec/kitchen/color_spec.rb +54 -54
  107. data/spec/kitchen/config_spec.rb +408 -408
  108. data/spec/kitchen/configurable_spec.rb +1095 -1062
  109. data/spec/kitchen/data_munger_spec.rb +2694 -2383
  110. data/spec/kitchen/diagnostic_spec.rb +129 -129
  111. data/spec/kitchen/driver/base_spec.rb +121 -121
  112. data/spec/kitchen/driver/dummy_spec.rb +199 -199
  113. data/spec/kitchen/driver/proxy_spec.rb +138 -138
  114. data/spec/kitchen/driver/ssh_base_spec.rb +1115 -1115
  115. data/spec/kitchen/driver_spec.rb +112 -112
  116. data/spec/kitchen/errors_spec.rb +309 -309
  117. data/spec/kitchen/instance_spec.rb +1419 -1419
  118. data/spec/kitchen/lazy_hash_spec.rb +117 -117
  119. data/spec/kitchen/loader/yaml_spec.rb +774 -774
  120. data/spec/kitchen/logger_spec.rb +429 -429
  121. data/spec/kitchen/logging_spec.rb +59 -59
  122. data/spec/kitchen/login_command_spec.rb +68 -68
  123. data/spec/kitchen/metadata_chopper_spec.rb +82 -82
  124. data/spec/kitchen/platform_spec.rb +89 -89
  125. data/spec/kitchen/provisioner/base_spec.rb +386 -386
  126. data/spec/kitchen/provisioner/chef_apply_spec.rb +136 -136
  127. data/spec/kitchen/provisioner/chef_base_spec.rb +1161 -1067
  128. data/spec/kitchen/provisioner/chef_solo_spec.rb +557 -557
  129. data/spec/kitchen/provisioner/chef_zero_spec.rb +1001 -1001
  130. data/spec/kitchen/provisioner/dummy_spec.rb +99 -99
  131. data/spec/kitchen/provisioner/shell_spec.rb +566 -566
  132. data/spec/kitchen/provisioner_spec.rb +107 -107
  133. data/spec/kitchen/shell_out_spec.rb +150 -150
  134. data/spec/kitchen/ssh_spec.rb +693 -693
  135. data/spec/kitchen/state_file_spec.rb +129 -129
  136. data/spec/kitchen/suite_spec.rb +62 -62
  137. data/spec/kitchen/transport/base_spec.rb +89 -89
  138. data/spec/kitchen/transport/ssh_spec.rb +1255 -1255
  139. data/spec/kitchen/transport/winrm_spec.rb +1143 -1143
  140. data/spec/kitchen/transport_spec.rb +112 -112
  141. data/spec/kitchen/util_spec.rb +165 -165
  142. data/spec/kitchen/verifier/base_spec.rb +362 -362
  143. data/spec/kitchen/verifier/busser_spec.rb +610 -610
  144. data/spec/kitchen/verifier/dummy_spec.rb +99 -99
  145. data/spec/kitchen/verifier/shell_spec.rb +160 -158
  146. data/spec/kitchen/verifier_spec.rb +120 -120
  147. data/spec/kitchen_spec.rb +114 -114
  148. data/spec/spec_helper.rb +85 -85
  149. data/spec/support/powershell_max_size_spec.rb +40 -40
  150. data/support/busser_install_command.ps1 +14 -14
  151. data/support/busser_install_command.sh +14 -14
  152. data/support/chef-client-zero.rb +77 -77
  153. data/support/chef_base_init_command.ps1 +18 -18
  154. data/support/chef_base_init_command.sh +2 -2
  155. data/support/chef_base_install_command.ps1 +85 -85
  156. data/support/chef_base_install_command.sh +229 -229
  157. data/support/chef_zero_prepare_command_legacy.ps1 +9 -9
  158. data/support/chef_zero_prepare_command_legacy.sh +10 -10
  159. data/support/download_helpers.sh +109 -109
  160. data/support/dummy-validation.pem +27 -27
  161. data/templates/driver/CHANGELOG.md.erb +3 -3
  162. data/templates/driver/Gemfile.erb +3 -3
  163. data/templates/driver/README.md.erb +64 -64
  164. data/templates/driver/Rakefile.erb +21 -21
  165. data/templates/driver/driver.rb.erb +23 -23
  166. data/templates/driver/gemspec.erb +29 -29
  167. data/templates/driver/gitignore.erb +17 -17
  168. data/templates/driver/license_apachev2.erb +15 -15
  169. data/templates/driver/license_lgplv3.erb +16 -16
  170. data/templates/driver/license_mit.erb +22 -22
  171. data/templates/driver/license_reserved.erb +5 -5
  172. data/templates/driver/tailor.erb +4 -4
  173. data/templates/driver/travis.yml.erb +11 -11
  174. data/templates/driver/version.rb.erb +12 -12
  175. data/templates/init/chefignore.erb +1 -1
  176. data/templates/init/kitchen.yml.erb +18 -18
  177. data/test-kitchen.gemspec +62 -62
  178. data/test/integration/default/default_spec.rb +3 -0
  179. data/testing_windows.md +37 -37
  180. metadata +23 -11
@@ -1,51 +1,51 @@
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 "kitchen/command"
20
-
21
- require "benchmark"
22
-
23
- module Kitchen
24
-
25
- module Command
26
-
27
- # Command to test one or more instances.
28
- #
29
- # @author Fletcher Nichol <fnichol@nichol.ca>
30
- class Test < Kitchen::Command::Base
31
-
32
- include RunAction
33
-
34
- # Invoke the command.
35
- def call
36
- if !%w[passing always never].include?(options[:destroy])
37
- raise ArgumentError, "Destroy mode must be passing, always, or never."
38
- end
39
-
40
- banner "Starting Kitchen (v#{Kitchen::VERSION})"
41
- elapsed = Benchmark.measure do
42
- destroy_mode = options[:destroy].to_sym
43
- results = parse_subcommand(args.join("|"))
44
-
45
- run_action(:test, results, destroy_mode)
46
- end
47
- banner "Kitchen is finished. #{Util.duration(elapsed.real)}"
48
- end
49
- end
50
- end
51
- 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 "kitchen/command"
20
+
21
+ require "benchmark"
22
+
23
+ module Kitchen
24
+
25
+ module Command
26
+
27
+ # Command to test one or more instances.
28
+ #
29
+ # @author Fletcher Nichol <fnichol@nichol.ca>
30
+ class Test < Kitchen::Command::Base
31
+
32
+ include RunAction
33
+
34
+ # Invoke the command.
35
+ def call
36
+ if !%w[passing always never].include?(options[:destroy])
37
+ raise ArgumentError, "Destroy mode must be passing, always, or never."
38
+ end
39
+
40
+ banner "Starting Kitchen (v#{Kitchen::VERSION})"
41
+ elapsed = Benchmark.measure do
42
+ destroy_mode = options[:destroy].to_sym
43
+ results = parse_subcommand(args.join("|"))
44
+
45
+ run_action(:test, results, destroy_mode)
46
+ end
47
+ banner "Kitchen is finished. #{Util.duration(elapsed.real)}"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,322 +1,322 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 2012, 2013, 2014, 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
- module Kitchen
20
-
21
- # Base configuration class for Kitchen. This class exposes configuration such
22
- # as the location of the Kitchen config file, instances, log_levels, etc.
23
- # This object is a factory object, meaning that it is responsible for
24
- # consuming the desired testing configuration in and returning Ruby objects
25
- # which are used to perfom the work.
26
- #
27
- # Most internal objects are created with the expectation of being
28
- # *immutable*, meaning that internal state cannot be modified after creation.
29
- # Any data manipulation or thread-unsafe activity is performed in this object
30
- # so that the subsequently created objects (such as Instances, Platforms,
31
- # Drivers, etc.) can safely run in concurrent threads of execution. To
32
- # prevent the re-creation of duplicate objects, most created objects are
33
- # memoized. The consequence of this is that once the Instance Array has
34
- # been requested (with the `#instances` message), you will always be returned
35
- # the same Instance objects.
36
- #
37
- # @example fetching all instances
38
- #
39
- # Kitchen::Config.new.instances
40
- #
41
- # @example fetching an instance by name
42
- #
43
- # Kitchen::Config.new.instances.get("default-ubuntu-12.04")
44
- #
45
- # @example fetching all instances matching a regular expression
46
- #
47
- # Kitchen::Config.new.instances.get_all(/ubuntu/)
48
- #
49
- # @author Fletcher Nichol <fnichol@nichol.ca>
50
- class Config
51
-
52
- # @return [String] the absolute path to the root of a Test Kitchen project
53
- # @api private
54
- attr_reader :kitchen_root
55
-
56
- # @return [String] the absolute path to the directory into which all Test
57
- # Kitchen log files will be written
58
- # @api private
59
- attr_reader :log_root
60
-
61
- # @return [String] the absolute path to the directory containing test
62
- # suites and other testing-related file and directories
63
- # @api private
64
- attr_reader :test_base_path
65
-
66
- # @return [#read] the data loader that responds to a `#read` message,
67
- # returning a Hash data structure
68
- # @api private
69
- attr_reader :loader
70
-
71
- # @return [Symbol] the logging verbosity level
72
- # @api private
73
- attr_accessor :log_level
74
-
75
- # @return [Boolean] whether to overwrite the log file when
76
- # Test Kitchen runs
77
- # @api private
78
- attr_accessor :log_overwrite
79
-
80
- # @return [String] an absolute path to the directory containing test suites
81
- # @api private
82
- attr_accessor :test_base_path
83
-
84
- # Creates a new configuration, representing a particular testing
85
- # configuration for a project.
86
- #
87
- # @param [Hash] options configuration
88
- # @option options [#read] :loader an object that responds to `#read` with
89
- # a Hash structure suitable for manipulating
90
- # (default: `Kitchen::Loader::YAML.new`)
91
- # @option options [String] :kitchen_root an absolute path to the root of a
92
- # Test Kitchen project, usually containing a `.kitchen.yml` file
93
- # (default `Dir.pwd`)
94
- # @option options [String] :log_root an absolute path to the directory
95
- # into which all Test Kitchen log files will be written
96
- # (default: `"#{kitchen_root}/.kitchen/logs"`)
97
- # @option options [String] :test_base_path an absolute path to the
98
- # directory containing test suites and other testing-related files and
99
- # directories (default: `"#{kitchen_root}/test/integration"`)
100
- # @option options [Symbol] :log_level the log level verbosity that the
101
- # loggers will use when outputing information (default: `:info`)
102
- def initialize(options = {})
103
- @loader = options.fetch(:loader) { Kitchen::Loader::YAML.new }
104
- @kitchen_root = options.fetch(:kitchen_root) { Dir.pwd }
105
- @log_level = options.fetch(:log_level) { Kitchen::DEFAULT_LOG_LEVEL }
106
- @log_overwrite = options.fetch(:log_overwrite) { Kitchen::DEFAULT_LOG_OVERWRITE }
107
- @log_root = options.fetch(:log_root) { default_log_root }
108
- @test_base_path = options.fetch(:test_base_path) { default_test_base_path }
109
- end
110
-
111
- # @return [Collection<Instance>] all instances, resulting from all
112
- # platform and suite combinations
113
- def instances
114
- @instances ||= Collection.new(build_instances)
115
- end
116
-
117
- # @return [Collection<Platform>] all defined platforms which will be used
118
- # in convergence integration
119
- def platforms
120
- @platforms ||= Collection.new(
121
- data.platform_data.map { |pdata| Platform.new(pdata) })
122
- end
123
-
124
- # @return [Collection<Suite>] all defined suites which will be used in
125
- # convergence integration
126
- def suites
127
- @suites ||= Collection.new(
128
- data.suite_data.map { |sdata| Suite.new(sdata) })
129
- end
130
-
131
- private
132
-
133
- # Builds the filtered list of Instance objects.
134
- #
135
- # @return [Array<Instance] an array of Instances
136
- # @api private
137
- def build_instances
138
- filter_instances.map.with_index do |(suite, platform), index|
139
- new_instance(suite, platform, index)
140
- end
141
- end
142
-
143
- # Returns an object which can generate configuration hashes for all the
144
- # primary Test Kitchen objects such as Drivers, Provisioners, etc.
145
- #
146
- # @return [DataMunger] a data manipulator
147
- # @api private
148
- def data
149
- @data ||= DataMunger.new(loader.read, kitchen_config)
150
- end
151
-
152
- # Determines the default absolute path to a log directory, based on the
153
- # value of `#kitchen_root`.
154
- #
155
- # @return [String] an absolute path to the log directory
156
- # @api private
157
- def default_log_root
158
- File.join(kitchen_root, Kitchen::DEFAULT_LOG_DIR)
159
- end
160
-
161
- # Determines the default absolute path to the testing files directory,
162
- # based on the the value of `#kitchen_root`.
163
- #
164
- # @return [String] an absolute path to the testing files directory
165
- # @api private
166
- def default_test_base_path
167
- File.join(kitchen_root, Kitchen::DEFAULT_TEST_DIR)
168
- end
169
-
170
- # Generates a filtered Array of tuples (Suite/Platform pairs) which is the
171
- # cartesian product of suites and platforms. A Suite has two optional
172
- # arrays (`#includes` and `#excludes`) which can be used to drop or
173
- # select certain Platforms with which to join.
174
- #
175
- # @return [Array<Array<Suite, Platform>>] an Array of Suite/Platform
176
- # tuples
177
- # @api private
178
- def filter_instances
179
- suites.product(platforms).select do |suite, platform|
180
- if !suite.includes.empty?
181
- suite.includes.include?(platform.name)
182
- elsif !suite.excludes.empty?
183
- !suite.excludes.include?(platform.name)
184
- else
185
- true
186
- end
187
- end
188
- end
189
-
190
- # Determines the String name for an Instance, given a Suite and a Platform.
191
- #
192
- # @param suite [Suite,#name] a Suite
193
- # @param platform [Platform,#name] a Platform
194
- # @return [String] an Instance name
195
- # @api private
196
- def instance_name(suite, platform)
197
- Instance.name_for(suite, platform)
198
- end
199
-
200
- # Generates the immutable Test Kitchen configuration and reasonable
201
- # defaults for Drivers, Provisioners and Transports.
202
- #
203
- # @return [Hash] a configuration Hash
204
- # @api private
205
- def kitchen_config
206
- @kitchen_config ||= {
207
- :defaults => {
208
- :driver => Driver::DEFAULT_PLUGIN,
209
- :provisioner => Provisioner::DEFAULT_PLUGIN,
210
- :verifier => Verifier::DEFAULT_PLUGIN,
211
- :transport => lambda { |_suite, platform|
212
- platform =~ /^win/i ? "winrm" : Transport::DEFAULT_PLUGIN
213
- }
214
- },
215
- :kitchen_root => kitchen_root,
216
- :test_base_path => test_base_path,
217
- :log_level => log_level,
218
- :log_overwrite => log_overwrite
219
- }
220
- end
221
-
222
- # Builds a newly configured Driver object, for a given Suite and Platform.
223
- #
224
- # @param suite [Suite,#name] a Suite
225
- # @param platform [Platform,#name] a Platform
226
- # @return [Driver] a new Driver object
227
- # @api private
228
- def new_driver(suite, platform)
229
- ddata = data.driver_data_for(suite.name, platform.name)
230
- Driver.for_plugin(ddata[:name], ddata)
231
- end
232
-
233
- # Builds a newly configured Instance object, for a given Suite and
234
- # Platform.
235
- #
236
- # @param suite [Suite,#name] a Suite
237
- # @param platform [Platform,#name] a Platform
238
- # @param index [Integer] an index used for colorizing output
239
- # @return [Instance] a new Instance object
240
- # @api private
241
- def new_instance(suite, platform, index)
242
- Instance.new(
243
- :driver => new_driver(suite, platform),
244
- :logger => new_instance_logger(suite, platform, index),
245
- :suite => suite,
246
- :platform => platform,
247
- :provisioner => new_provisioner(suite, platform),
248
- :transport => new_transport(suite, platform),
249
- :verifier => new_verifier(suite, platform),
250
- :state_file => new_state_file(suite, platform)
251
- )
252
- end
253
-
254
- # Builds a newly configured Logger object, for a given Suite and
255
- # Platform.
256
- #
257
- # @param suite [Suite,#name] a Suite
258
- # @param platform [Platform,#name] a Platform
259
- # @param index [Integer] an index used for colorizing output
260
- # @return [Logger] a new Logger object
261
- # @api private
262
- def new_instance_logger(suite, platform, index)
263
- name = instance_name(suite, platform)
264
- log_location = File.join(log_root, "#{name}.log").to_s
265
- Logger.new(
266
- :stdout => STDOUT,
267
- :color => Color::COLORS[index % Color::COLORS.size].to_sym,
268
- :logdev => log_location,
269
- :level => Util.to_logger_level(log_level),
270
- :log_overwrite => log_overwrite,
271
- :progname => name
272
- )
273
- end
274
-
275
- # Builds a newly configured Provisioner object, for a given Suite and
276
- # Platform.
277
- #
278
- # @param suite [Suite,#name] a Suite
279
- # @param platform [Platform,#name] a Platform
280
- # @return [Provisioner] a new Provisioner object
281
- # @api private
282
- def new_provisioner(suite, platform)
283
- pdata = data.provisioner_data_for(suite.name, platform.name)
284
- Provisioner.for_plugin(pdata[:name], pdata)
285
- end
286
-
287
- # Builds a newly configured StateFile object, for a given Suite and
288
- # Platform.
289
- #
290
- # @param suite [Suite,#name] a Suite
291
- # @param platform [Platform,#name] a Platform
292
- # @return [StateFile] a new StateFile object
293
- # @api private
294
- def new_state_file(suite, platform)
295
- StateFile.new(kitchen_root, instance_name(suite, platform))
296
- end
297
-
298
- # Builds a newly configured Transport object, for a given Suite and
299
- # Platform.
300
- #
301
- # @param suite [Suite,#name] a Suite
302
- # @param platform [Platform,#name] a Platform
303
- # @return [Transport] a new Transport object
304
- # @api private
305
- def new_transport(suite, platform)
306
- tdata = data.transport_data_for(suite.name, platform.name)
307
- Transport.for_plugin(tdata[:name], tdata)
308
- end
309
-
310
- # Builds a newly configured Verifier object, for a given a Suite and
311
- # Platform.
312
- #
313
- # @param suite [Suite,#name] a Suite
314
- # @param platform [Platform,#name] a Platform
315
- # @return [Verifier] a new Verifier object
316
- # @api private
317
- def new_verifier(suite, platform)
318
- vdata = data.verifier_data_for(suite.name, platform.name)
319
- Verifier.for_plugin(vdata[:name], vdata)
320
- end
321
- end
322
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2012, 2013, 2014, 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
+ module Kitchen
20
+
21
+ # Base configuration class for Kitchen. This class exposes configuration such
22
+ # as the location of the Kitchen config file, instances, log_levels, etc.
23
+ # This object is a factory object, meaning that it is responsible for
24
+ # consuming the desired testing configuration in and returning Ruby objects
25
+ # which are used to perfom the work.
26
+ #
27
+ # Most internal objects are created with the expectation of being
28
+ # *immutable*, meaning that internal state cannot be modified after creation.
29
+ # Any data manipulation or thread-unsafe activity is performed in this object
30
+ # so that the subsequently created objects (such as Instances, Platforms,
31
+ # Drivers, etc.) can safely run in concurrent threads of execution. To
32
+ # prevent the re-creation of duplicate objects, most created objects are
33
+ # memoized. The consequence of this is that once the Instance Array has
34
+ # been requested (with the `#instances` message), you will always be returned
35
+ # the same Instance objects.
36
+ #
37
+ # @example fetching all instances
38
+ #
39
+ # Kitchen::Config.new.instances
40
+ #
41
+ # @example fetching an instance by name
42
+ #
43
+ # Kitchen::Config.new.instances.get("default-ubuntu-12.04")
44
+ #
45
+ # @example fetching all instances matching a regular expression
46
+ #
47
+ # Kitchen::Config.new.instances.get_all(/ubuntu/)
48
+ #
49
+ # @author Fletcher Nichol <fnichol@nichol.ca>
50
+ class Config
51
+
52
+ # @return [String] the absolute path to the root of a Test Kitchen project
53
+ # @api private
54
+ attr_reader :kitchen_root
55
+
56
+ # @return [String] the absolute path to the directory into which all Test
57
+ # Kitchen log files will be written
58
+ # @api private
59
+ attr_reader :log_root
60
+
61
+ # @return [String] the absolute path to the directory containing test
62
+ # suites and other testing-related file and directories
63
+ # @api private
64
+ attr_reader :test_base_path
65
+
66
+ # @return [#read] the data loader that responds to a `#read` message,
67
+ # returning a Hash data structure
68
+ # @api private
69
+ attr_reader :loader
70
+
71
+ # @return [Symbol] the logging verbosity level
72
+ # @api private
73
+ attr_accessor :log_level
74
+
75
+ # @return [Boolean] whether to overwrite the log file when
76
+ # Test Kitchen runs
77
+ # @api private
78
+ attr_accessor :log_overwrite
79
+
80
+ # @return [String] an absolute path to the directory containing test suites
81
+ # @api private
82
+ attr_accessor :test_base_path
83
+
84
+ # Creates a new configuration, representing a particular testing
85
+ # configuration for a project.
86
+ #
87
+ # @param [Hash] options configuration
88
+ # @option options [#read] :loader an object that responds to `#read` with
89
+ # a Hash structure suitable for manipulating
90
+ # (default: `Kitchen::Loader::YAML.new`)
91
+ # @option options [String] :kitchen_root an absolute path to the root of a
92
+ # Test Kitchen project, usually containing a `.kitchen.yml` file
93
+ # (default `Dir.pwd`)
94
+ # @option options [String] :log_root an absolute path to the directory
95
+ # into which all Test Kitchen log files will be written
96
+ # (default: `"#{kitchen_root}/.kitchen/logs"`)
97
+ # @option options [String] :test_base_path an absolute path to the
98
+ # directory containing test suites and other testing-related files and
99
+ # directories (default: `"#{kitchen_root}/test/integration"`)
100
+ # @option options [Symbol] :log_level the log level verbosity that the
101
+ # loggers will use when outputing information (default: `:info`)
102
+ def initialize(options = {})
103
+ @loader = options.fetch(:loader) { Kitchen::Loader::YAML.new }
104
+ @kitchen_root = options.fetch(:kitchen_root) { Dir.pwd }
105
+ @log_level = options.fetch(:log_level) { Kitchen::DEFAULT_LOG_LEVEL }
106
+ @log_overwrite = options.fetch(:log_overwrite) { Kitchen::DEFAULT_LOG_OVERWRITE }
107
+ @log_root = options.fetch(:log_root) { default_log_root }
108
+ @test_base_path = options.fetch(:test_base_path) { default_test_base_path }
109
+ end
110
+
111
+ # @return [Collection<Instance>] all instances, resulting from all
112
+ # platform and suite combinations
113
+ def instances
114
+ @instances ||= Collection.new(build_instances)
115
+ end
116
+
117
+ # @return [Collection<Platform>] all defined platforms which will be used
118
+ # in convergence integration
119
+ def platforms
120
+ @platforms ||= Collection.new(
121
+ data.platform_data.map { |pdata| Platform.new(pdata) })
122
+ end
123
+
124
+ # @return [Collection<Suite>] all defined suites which will be used in
125
+ # convergence integration
126
+ def suites
127
+ @suites ||= Collection.new(
128
+ data.suite_data.map { |sdata| Suite.new(sdata) })
129
+ end
130
+
131
+ private
132
+
133
+ # Builds the filtered list of Instance objects.
134
+ #
135
+ # @return [Array<Instance] an array of Instances
136
+ # @api private
137
+ def build_instances
138
+ filter_instances.map.with_index do |(suite, platform), index|
139
+ new_instance(suite, platform, index)
140
+ end
141
+ end
142
+
143
+ # Returns an object which can generate configuration hashes for all the
144
+ # primary Test Kitchen objects such as Drivers, Provisioners, etc.
145
+ #
146
+ # @return [DataMunger] a data manipulator
147
+ # @api private
148
+ def data
149
+ @data ||= DataMunger.new(loader.read, kitchen_config)
150
+ end
151
+
152
+ # Determines the default absolute path to a log directory, based on the
153
+ # value of `#kitchen_root`.
154
+ #
155
+ # @return [String] an absolute path to the log directory
156
+ # @api private
157
+ def default_log_root
158
+ File.join(kitchen_root, Kitchen::DEFAULT_LOG_DIR)
159
+ end
160
+
161
+ # Determines the default absolute path to the testing files directory,
162
+ # based on the the value of `#kitchen_root`.
163
+ #
164
+ # @return [String] an absolute path to the testing files directory
165
+ # @api private
166
+ def default_test_base_path
167
+ File.join(kitchen_root, Kitchen::DEFAULT_TEST_DIR)
168
+ end
169
+
170
+ # Generates a filtered Array of tuples (Suite/Platform pairs) which is the
171
+ # cartesian product of suites and platforms. A Suite has two optional
172
+ # arrays (`#includes` and `#excludes`) which can be used to drop or
173
+ # select certain Platforms with which to join.
174
+ #
175
+ # @return [Array<Array<Suite, Platform>>] an Array of Suite/Platform
176
+ # tuples
177
+ # @api private
178
+ def filter_instances
179
+ suites.product(platforms).select do |suite, platform|
180
+ if !suite.includes.empty?
181
+ suite.includes.include?(platform.name)
182
+ elsif !suite.excludes.empty?
183
+ !suite.excludes.include?(platform.name)
184
+ else
185
+ true
186
+ end
187
+ end
188
+ end
189
+
190
+ # Determines the String name for an Instance, given a Suite and a Platform.
191
+ #
192
+ # @param suite [Suite,#name] a Suite
193
+ # @param platform [Platform,#name] a Platform
194
+ # @return [String] an Instance name
195
+ # @api private
196
+ def instance_name(suite, platform)
197
+ Instance.name_for(suite, platform)
198
+ end
199
+
200
+ # Generates the immutable Test Kitchen configuration and reasonable
201
+ # defaults for Drivers, Provisioners and Transports.
202
+ #
203
+ # @return [Hash] a configuration Hash
204
+ # @api private
205
+ def kitchen_config
206
+ @kitchen_config ||= {
207
+ :defaults => {
208
+ :driver => Driver::DEFAULT_PLUGIN,
209
+ :provisioner => Provisioner::DEFAULT_PLUGIN,
210
+ :verifier => Verifier::DEFAULT_PLUGIN,
211
+ :transport => lambda { |_suite, platform|
212
+ platform =~ /^win/i ? "winrm" : Transport::DEFAULT_PLUGIN
213
+ }
214
+ },
215
+ :kitchen_root => kitchen_root,
216
+ :test_base_path => test_base_path,
217
+ :log_level => log_level,
218
+ :log_overwrite => log_overwrite
219
+ }
220
+ end
221
+
222
+ # Builds a newly configured Driver object, for a given Suite and Platform.
223
+ #
224
+ # @param suite [Suite,#name] a Suite
225
+ # @param platform [Platform,#name] a Platform
226
+ # @return [Driver] a new Driver object
227
+ # @api private
228
+ def new_driver(suite, platform)
229
+ ddata = data.driver_data_for(suite.name, platform.name)
230
+ Driver.for_plugin(ddata[:name], ddata)
231
+ end
232
+
233
+ # Builds a newly configured Instance object, for a given Suite and
234
+ # Platform.
235
+ #
236
+ # @param suite [Suite,#name] a Suite
237
+ # @param platform [Platform,#name] a Platform
238
+ # @param index [Integer] an index used for colorizing output
239
+ # @return [Instance] a new Instance object
240
+ # @api private
241
+ def new_instance(suite, platform, index)
242
+ Instance.new(
243
+ :driver => new_driver(suite, platform),
244
+ :logger => new_instance_logger(suite, platform, index),
245
+ :suite => suite,
246
+ :platform => platform,
247
+ :provisioner => new_provisioner(suite, platform),
248
+ :transport => new_transport(suite, platform),
249
+ :verifier => new_verifier(suite, platform),
250
+ :state_file => new_state_file(suite, platform)
251
+ )
252
+ end
253
+
254
+ # Builds a newly configured Logger object, for a given Suite and
255
+ # Platform.
256
+ #
257
+ # @param suite [Suite,#name] a Suite
258
+ # @param platform [Platform,#name] a Platform
259
+ # @param index [Integer] an index used for colorizing output
260
+ # @return [Logger] a new Logger object
261
+ # @api private
262
+ def new_instance_logger(suite, platform, index)
263
+ name = instance_name(suite, platform)
264
+ log_location = File.join(log_root, "#{name}.log").to_s
265
+ Logger.new(
266
+ :stdout => STDOUT,
267
+ :color => Color::COLORS[index % Color::COLORS.size].to_sym,
268
+ :logdev => log_location,
269
+ :level => Util.to_logger_level(log_level),
270
+ :log_overwrite => log_overwrite,
271
+ :progname => name
272
+ )
273
+ end
274
+
275
+ # Builds a newly configured Provisioner object, for a given Suite and
276
+ # Platform.
277
+ #
278
+ # @param suite [Suite,#name] a Suite
279
+ # @param platform [Platform,#name] a Platform
280
+ # @return [Provisioner] a new Provisioner object
281
+ # @api private
282
+ def new_provisioner(suite, platform)
283
+ pdata = data.provisioner_data_for(suite.name, platform.name)
284
+ Provisioner.for_plugin(pdata[:name], pdata)
285
+ end
286
+
287
+ # Builds a newly configured StateFile object, for a given Suite and
288
+ # Platform.
289
+ #
290
+ # @param suite [Suite,#name] a Suite
291
+ # @param platform [Platform,#name] a Platform
292
+ # @return [StateFile] a new StateFile object
293
+ # @api private
294
+ def new_state_file(suite, platform)
295
+ StateFile.new(kitchen_root, instance_name(suite, platform))
296
+ end
297
+
298
+ # Builds a newly configured Transport object, for a given Suite and
299
+ # Platform.
300
+ #
301
+ # @param suite [Suite,#name] a Suite
302
+ # @param platform [Platform,#name] a Platform
303
+ # @return [Transport] a new Transport object
304
+ # @api private
305
+ def new_transport(suite, platform)
306
+ tdata = data.transport_data_for(suite.name, platform.name)
307
+ Transport.for_plugin(tdata[:name], tdata)
308
+ end
309
+
310
+ # Builds a newly configured Verifier object, for a given a Suite and
311
+ # Platform.
312
+ #
313
+ # @param suite [Suite,#name] a Suite
314
+ # @param platform [Platform,#name] a Platform
315
+ # @return [Verifier] a new Verifier object
316
+ # @api private
317
+ def new_verifier(suite, platform)
318
+ vdata = data.verifier_data_for(suite.name, platform.name)
319
+ Verifier.for_plugin(vdata[:name], vdata)
320
+ end
321
+ end
322
+ end