test-kitchen 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
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