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,529 +1,529 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 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
- require "thor/util"
20
-
21
- require "kitchen/lazy_hash"
22
-
23
- module Kitchen
24
-
25
- # A mixin for providing configuration-related behavior such as default
26
- # config (static, computed, inherited), required config, local path
27
- # expansion, etc.
28
- #
29
- # @author Fletcher Nichol <fnichol@nichol.ca>
30
- module Configurable
31
-
32
- def self.included(base)
33
- base.extend(ClassMethods)
34
- end
35
-
36
- # @return [Kitchen::Instance] the associated instance
37
- attr_reader :instance
38
-
39
- # A lifecycle method that should be invoked when the object is about ready
40
- # to be used. A reference to an Instance is required as configuration
41
- # dependant data may be access through an Instance. This also acts as a
42
- # hook point where the object may wish to perform other last minute
43
- # checks, validations, or configuration expansions.
44
- #
45
- # @param instance [Instance] an associated instance
46
- # @return [self] itself, for use in chaining
47
- # @raise [ClientError] if instance parameter is nil
48
- def finalize_config!(instance)
49
- if instance.nil?
50
- raise ClientError, "Instance must be provided to #{self}"
51
- end
52
-
53
- @instance = instance
54
- expand_paths!
55
- validate_config!
56
- load_needed_dependencies!
57
-
58
- self
59
- end
60
-
61
- # Provides hash-like access to configuration keys.
62
- #
63
- # @param attr [Object] configuration key
64
- # @return [Object] value at configuration key
65
- def [](attr)
66
- config[attr]
67
- end
68
-
69
- # @return [TrueClass,FalseClass] true if `:shell_type` is `"bourne"` (or
70
- # unset, for backwards compatability)
71
- def bourne_shell?
72
- ["bourne", nil].include?(instance.platform.shell_type)
73
- end
74
-
75
- # Find an appropriate path to a file or directory, based on graceful
76
- # fallback rules or returns nil if path cannot be determined.
77
- #
78
- # Given an instance with suite named `"server"`, a `test_base_path` of
79
- # `"/a/b"`, and a path segement of `"roles"` then following will be tried
80
- # in order (first match that exists wins):
81
- #
82
- # 1. /a/b/server/roles
83
- # 2. /a/b/roles
84
- # 3. $PWD/roles
85
- #
86
- # @param path [String] the base path segment to search for
87
- # @param opts [Hash] options
88
- # @option opts [Symbol] :type either `:file` or `:directory` (default)
89
- # @option opts [Symbol] :base_path a custom base path to search under,
90
- # default uses value from `config[:test_base_path]`
91
- # @return [String] path to the existing file or directory, or nil if file
92
- # or directory was not found
93
- # @raise [UserError] if `config[:test_base_path]` is used and is not set
94
- def calculate_path(path, opts = {})
95
- type = opts.fetch(:type, :directory)
96
- base = opts.fetch(:base_path) do
97
- config.fetch(:test_base_path) do |key|
98
- raise UserError, "#{key} is not found in #{self}"
99
- end
100
- end
101
-
102
- [
103
- File.join(base, instance.suite.name, path),
104
- File.join(base, path),
105
- File.join(Dir.pwd, path)
106
- ].find do |candidate|
107
- type == :directory ? File.directory?(candidate) : File.file?(candidate)
108
- end
109
- end
110
-
111
- # Returns an array of configuration keys.
112
- #
113
- # @return [Array] array of configuration keys
114
- def config_keys
115
- config.keys
116
- end
117
-
118
- # Returns a Hash of configuration and other useful diagnostic information.
119
- #
120
- # @return [Hash] a diagnostic hash
121
- def diagnose
122
- result = Hash.new
123
- config_keys.sort.each { |k| result[k] = config[k] }
124
- result
125
- end
126
-
127
- # Returns a Hash of configuration and other useful diagnostic information
128
- # associated with the plugin itself (such as loaded version, class name,
129
- # etc.).
130
- #
131
- # @return [Hash] a diagnostic hash
132
- def diagnose_plugin
133
- result = Hash.new
134
- result[:name] = name
135
- result.merge!(self.class.diagnose)
136
- result
137
- end
138
-
139
- # Returns the name of this plugin, suitable for display in a CLI.
140
- #
141
- # @return [String] name of this plugin
142
- def name
143
- self.class.name.split("::").last
144
- end
145
-
146
- # @return [TrueClass,FalseClass] true if `:shell_type` is `"powershell"`
147
- def powershell_shell?
148
- ["powershell"].include?(instance.platform.shell_type)
149
- end
150
-
151
- # Builds a file path based on the `:os_type` (`"windows"` or `"unix"`).
152
- #
153
- # @return [String] joined path for instance's os_type
154
- def remote_path_join(*parts)
155
- path = File.join(*parts)
156
- windows_os? ? path.gsub("/", "\\") : path.gsub("\\", "/")
157
- end
158
-
159
- # @return [TrueClass,FalseClass] true if `:os_type` is `"unix"` (or
160
- # unset, for backwards compatibility)
161
- def unix_os?
162
- ["unix", nil].include?(instance.platform.os_type)
163
- end
164
-
165
- # Performs whatever tests that may be required to ensure that this plugin
166
- # will be able to function in the current environment. This may involve
167
- # checking for the presence of certain directories, software installed,
168
- # etc.
169
- #
170
- # @raise [UserError] if the plugin will not be able to perform or if a
171
- # documented dependency is missing from the system
172
- def verify_dependencies
173
- # this method may be left unimplemented if that is applicable
174
- end
175
-
176
- # @return [TrueClass,FalseClass] true if `:os_type` is `"windows"`
177
- def windows_os?
178
- ["windows"].include?(instance.platform.os_type)
179
- end
180
-
181
- private
182
-
183
- # @return [LzayHash] a configuration hash
184
- # @api private
185
- attr_reader :config
186
-
187
- # Initializes an internal configuration hash. The hash may contain
188
- # callable blocks as values that are meant to be called lazily. This
189
- # method is intended to be included in an object's .initialize method.
190
- #
191
- # @param config [Hash] initial provided configuration
192
- # @api private
193
- def init_config(config)
194
- @config = LazyHash.new(config, self)
195
- self.class.defaults.each do |attr, value|
196
- @config[attr] = value unless @config.key?(attr)
197
- end
198
- end
199
-
200
- # Expands file paths for certain configuration values. A configuration
201
- # value is marked for file expansion with a expand_path_for declaration
202
- # in the included class.
203
- #
204
- # @api private
205
- def expand_paths!
206
- root_path = config[:kitchen_root] || Dir.pwd
207
- expanded_paths = LazyHash.new(self.class.expanded_paths, self).to_hash
208
-
209
- expanded_paths.each do |key, should_expand|
210
- next if !should_expand || config[key].nil? || config[key] == false
211
-
212
- config[key] = if config[key].is_a?(Array)
213
- config[key].map { |path| File.expand_path(path, root_path) }
214
- else
215
- File.expand_path(config[key], root_path)
216
- end
217
- end
218
- end
219
-
220
- # Loads any required third party Ruby libraries or runs any shell out
221
- # commands to prepare the plugin. This method will be called in the
222
- # context of the main thread of execution and so does not necessarily
223
- # have to be thread safe.
224
- #
225
- # **Note:** any subclasses overriding this method would be well advised
226
- # to call super when overriding this method, for example:
227
- #
228
- # @example overriding `#load_needed_dependencies!`
229
- #
230
- # class MyProvisioner < Kitchen::Provisioner::Base
231
- # def load_needed_dependencies!
232
- # super
233
- # # any further work
234
- # end
235
- # end
236
- #
237
- # @raise [ClientError] if any library loading fails or any of the
238
- # dependency requirements cannot be satisfied
239
- # @api private
240
- def load_needed_dependencies!
241
- # this method may be left unimplemented if that is applicable
242
- end
243
-
244
- # @return [Logger] the instance's logger or Test Kitchen's common logger
245
- # otherwise
246
- # @api private
247
- def logger
248
- instance ? instance.logger : Kitchen.logger
249
- end
250
-
251
- # Builds a shell environment variable assignment string for the
252
- # required shell type.
253
- #
254
- # @param name [String] variable name
255
- # @param value [String] variable value
256
- # @return [String] shell variable assignment
257
- # @api private
258
- def shell_env_var(name, value)
259
- if powershell_shell?
260
- shell_var("env:#{name}", value)
261
- else
262
- "#{shell_var(name, value)}; export #{name}"
263
- end
264
- end
265
-
266
- # Builds a shell variable assignment string for the required shell type.
267
- #
268
- # @param name [String] variable name
269
- # @param value [String] variable value
270
- # @return [String] shell variable assignment
271
- # @api private
272
- def shell_var(name, value)
273
- if powershell_shell?
274
- %{$#{name} = "#{value}"}
275
- else
276
- %{#{name}="#{value}"}
277
- end
278
- end
279
-
280
- # Runs all validations set up for the included class. Each validation is
281
- # for a specific configuration attribute and has an associated callable
282
- # block. Each validation block is called with the attribute, its value,
283
- # and the included object for context.
284
- #
285
- # @api private
286
- def validate_config!
287
- self.class.validations.each do |attr, block|
288
- block.call(attr, config[attr], self)
289
- end
290
- end
291
-
292
- # Wraps a body of shell code with common context appropriate for the type
293
- # of shell.
294
- #
295
- # @param code [String] the shell code to be wrapped
296
- # @return [String] wrapped shell code
297
- # @api private
298
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
299
- # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
300
- def wrap_shell_code(code)
301
- env = []
302
- if config[:http_proxy] && !config[:http_proxy].empty?
303
- env << shell_env_var("http_proxy", config[:http_proxy])
304
- env << shell_env_var("HTTP_PROXY", config[:http_proxy])
305
- else
306
- export_proxy(env, "http")
307
- end
308
- if config[:https_proxy] && !config[:https_proxy].empty?
309
- env << shell_env_var("https_proxy", config[:https_proxy])
310
- env << shell_env_var("HTTPS_PROXY", config[:https_proxy])
311
- else
312
- export_proxy(env, "https")
313
- end
314
- if config[:ftp_proxy] && !config[:ftp_proxy].empty?
315
- env << shell_env_var("ftp_proxy", config[:ftp_proxy])
316
- env << shell_env_var("FTP_PROXY", config[:ftp_proxy])
317
- else
318
- export_proxy(env, "ftp")
319
- end
320
- # if http_proxy was set from environment variable or https_proxy was set
321
- # from environment variable, or ftp_proxy was set from environment
322
- # variable, include no_proxy environment variable, if set.
323
- if (!config[:http_proxy] && (ENV["http_proxy"] || ENV["HTTP_PROXY"])) ||
324
- (!config[:https_proxy] && (ENV["https_proxy"] || ENV["HTTPS_PROXY"])) ||
325
- (!config[:ftp_proxy] && (ENV["ftp_proxy"] || ENV["FTP_PROXY"]))
326
- env << shell_env_var("no_proxy", ENV["no_proxy"]) if ENV["no_proxy"]
327
- env << shell_env_var("NO_PROXY", ENV["NO_PROXY"]) if ENV["NO_PROXY"]
328
- end
329
- if powershell_shell?
330
- env.join("\n").concat("\n").concat(code)
331
- else
332
- Util.wrap_command(env.join("\n").concat("\n").concat(code))
333
- end
334
- end
335
-
336
- # Helper method to export
337
- #
338
- # @param env [Array] the environment to modify
339
- # @param code [String] the type of proxy to export, one of 'http', 'https' or 'ftp'
340
- # @api private
341
- def export_proxy(env, type)
342
- env << shell_env_var("#{type}_proxy", ENV["#{type}_proxy"]) if ENV["#{type}_proxy"]
343
- env << shell_env_var("#{type.upcase}_PROXY", ENV["#{type.upcase}_PROXY"]) if
344
- ENV["#{type.upcase}_PROXY"]
345
- end
346
-
347
- # Class methods which will be mixed in on inclusion of Configurable module.
348
- module ClassMethods
349
-
350
- # Sets the loaded version of this plugin, usually corresponding to the
351
- # RubyGems version of the plugin's library. If the plugin does not set
352
- # this value, then `nil` will be used and reported.
353
- #
354
- # @example setting a version used by RubyGems
355
- #
356
- # require "kitchen/driver/vagrant_version"
357
- #
358
- # module Kitchen
359
- # module Driver
360
- # class Vagrant < Kitchen::Driver::Base
361
- #
362
- # plugin_version Kitchen::Driver::VAGRANT_VERSION
363
- #
364
- # end
365
- # end
366
- # end
367
- #
368
- # @param version [String] a version string
369
- def plugin_version(version) # rubocop:disable Style/TrivialAccessors
370
- @plugin_version = version
371
- end
372
-
373
- # Returns a Hash of configuration and other useful diagnostic
374
- # information.
375
- #
376
- # @return [Hash] a diagnostic hash
377
- def diagnose
378
- {
379
- :class => name,
380
- :version => @plugin_version,
381
- :api_version => @api_version
382
- }
383
- end
384
-
385
- # Sets a sane default value for a configuration attribute. These values
386
- # can be overridden by provided configuration or in a subclass with
387
- # another default_config declaration.
388
- #
389
- # @example a nil default value
390
- #
391
- # default_config :i_am_nil
392
- #
393
- # @example a primitive default value
394
- #
395
- # default_config :use_sudo, true
396
- #
397
- # @example a block to compute a default value
398
- #
399
- # default_config :box_name do |subject|
400
- # subject.instance.platform.name
401
- # end
402
- #
403
- # @param attr [String] configuration attribute name
404
- # @param value [Object, nil] static default value for attribute
405
- # @yieldparam object [Object] a reference to the instantiated object
406
- # @yieldreturn [Object, nil] dynamically computed value for the attribute
407
- def default_config(attr, value = nil, &block)
408
- defaults[attr] = block_given? ? block : value
409
- end
410
-
411
- # Ensures that an attribute which is a path will be fully expanded at
412
- # the right time. This helps make the configuration unambiguous and much
413
- # easier to debug and diagnose.
414
- #
415
- # Note that the file path expansion is only intended for paths on the
416
- # local workstation invking the Test Kitchen code.
417
- #
418
- # @example the default usage
419
- #
420
- # expand_path_for :data_path
421
- #
422
- # @example disabling path expansion with a falsey value
423
- #
424
- # expand_path_for :relative_path, false
425
- #
426
- # @example using a block to determine whether or not to expand
427
- #
428
- # expand_path_for :relative_or_not_path do |subject|
429
- # subject.instance.name =~ /default/
430
- # end
431
- #
432
- # @param attr [String] configuration attribute name
433
- # @param value [Object, nil] whether or not to exand the file path
434
- # @yieldparam object [Object] a reference to the instantiated object
435
- # @yieldreturn [Object, nil] dynamically compute whether or not to
436
- # perform the file expansion
437
- def expand_path_for(attr, value = true, &block)
438
- expanded_paths[attr] = block_given? ? block : value
439
- end
440
-
441
- # Ensures that an attribute must have a non-nil, non-empty String value.
442
- # The default behavior will be to raise a user error and thereby halting
443
- # further configuration processing. Good use cases for require_config
444
- # might be cloud provider credential keys and other similar data.
445
- #
446
- # @example a value that must not be nil or an empty String
447
- #
448
- # required_config :cloud_api_token
449
- #
450
- # @example using a block to use custom validation logic
451
- #
452
- # required_config :email do |attr, value, subject|
453
- # raise UserError, "Must be an email address" unless value =~ /@/
454
- # end
455
- #
456
- # @param attr [String] configuration attribute name
457
- # @yieldparam attr [Symbol] the attribute name
458
- # @yieldparam value [Object] the current value of the attribute
459
- # @yieldparam object [Object] a reference to the instantiated object
460
- def required_config(attr, &block)
461
- if !block_given?
462
- klass = self
463
- block = lambda do |_, value, thing|
464
- if value.nil? || value.to_s.empty?
465
- attribute = "#{klass}#{thing.instance.to_str}#config[:#{attr}]"
466
- raise UserError, "#{attribute} cannot be blank"
467
- end
468
- end
469
- end
470
- validations[attr] = block
471
- end
472
-
473
- # @return [Hash] a hash of attribute keys and default values which has
474
- # been merged with any superclass defaults
475
- # @api private
476
- def defaults
477
- @defaults ||= Hash.new.merge(super_defaults)
478
- end
479
-
480
- # @return [Hash] a hash of defaults from the included class' superclass
481
- # if defined in the superclass, or an empty hash otherwise
482
- # @api private
483
- def super_defaults
484
- if superclass.respond_to?(:defaults)
485
- superclass.defaults
486
- else
487
- Hash.new
488
- end
489
- end
490
-
491
- # @return [Hash] a hash of attribute keys and truthy/falsey values to
492
- # determine if said attribute needs to be fully file path expanded,
493
- # which has been merged with any superclass expanded paths
494
- # @api private
495
- def expanded_paths
496
- @expanded_paths ||= Hash.new.merge(super_expanded_paths)
497
- end
498
-
499
- # @return [Hash] a hash of expanded paths from the included class'
500
- # superclass if defined in the superclass, or an empty hash otherwise
501
- # @api private
502
- def super_expanded_paths
503
- if superclass.respond_to?(:expanded_paths)
504
- superclass.expanded_paths
505
- else
506
- Hash.new
507
- end
508
- end
509
-
510
- # @return [Hash] a hash of attribute keys and valudation callable blocks
511
- # which has been merged with any superclass valudations
512
- # @api private
513
- def validations
514
- @validations ||= Hash.new.merge(super_validations)
515
- end
516
-
517
- # @return [Hash] a hash of validations from the included class'
518
- # superclass if defined in the superclass, or an empty hash otherwise
519
- # @api private
520
- def super_validations
521
- if superclass.respond_to?(:validations)
522
- superclass.validations
523
- else
524
- Hash.new
525
- end
526
- end
527
- end
528
- end
529
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 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
+ require "thor/util"
20
+
21
+ require "kitchen/lazy_hash"
22
+
23
+ module Kitchen
24
+
25
+ # A mixin for providing configuration-related behavior such as default
26
+ # config (static, computed, inherited), required config, local path
27
+ # expansion, etc.
28
+ #
29
+ # @author Fletcher Nichol <fnichol@nichol.ca>
30
+ module Configurable
31
+
32
+ def self.included(base)
33
+ base.extend(ClassMethods)
34
+ end
35
+
36
+ # @return [Kitchen::Instance] the associated instance
37
+ attr_reader :instance
38
+
39
+ # A lifecycle method that should be invoked when the object is about ready
40
+ # to be used. A reference to an Instance is required as configuration
41
+ # dependant data may be access through an Instance. This also acts as a
42
+ # hook point where the object may wish to perform other last minute
43
+ # checks, validations, or configuration expansions.
44
+ #
45
+ # @param instance [Instance] an associated instance
46
+ # @return [self] itself, for use in chaining
47
+ # @raise [ClientError] if instance parameter is nil
48
+ def finalize_config!(instance)
49
+ if instance.nil?
50
+ raise ClientError, "Instance must be provided to #{self}"
51
+ end
52
+
53
+ @instance = instance
54
+ expand_paths!
55
+ validate_config!
56
+ load_needed_dependencies!
57
+
58
+ self
59
+ end
60
+
61
+ # Provides hash-like access to configuration keys.
62
+ #
63
+ # @param attr [Object] configuration key
64
+ # @return [Object] value at configuration key
65
+ def [](attr)
66
+ config[attr]
67
+ end
68
+
69
+ # @return [TrueClass,FalseClass] true if `:shell_type` is `"bourne"` (or
70
+ # unset, for backwards compatability)
71
+ def bourne_shell?
72
+ ["bourne", nil].include?(instance.platform.shell_type)
73
+ end
74
+
75
+ # Find an appropriate path to a file or directory, based on graceful
76
+ # fallback rules or returns nil if path cannot be determined.
77
+ #
78
+ # Given an instance with suite named `"server"`, a `test_base_path` of
79
+ # `"/a/b"`, and a path segement of `"roles"` then following will be tried
80
+ # in order (first match that exists wins):
81
+ #
82
+ # 1. /a/b/server/roles
83
+ # 2. /a/b/roles
84
+ # 3. $PWD/roles
85
+ #
86
+ # @param path [String] the base path segment to search for
87
+ # @param opts [Hash] options
88
+ # @option opts [Symbol] :type either `:file` or `:directory` (default)
89
+ # @option opts [Symbol] :base_path a custom base path to search under,
90
+ # default uses value from `config[:test_base_path]`
91
+ # @return [String] path to the existing file or directory, or nil if file
92
+ # or directory was not found
93
+ # @raise [UserError] if `config[:test_base_path]` is used and is not set
94
+ def calculate_path(path, opts = {})
95
+ type = opts.fetch(:type, :directory)
96
+ base = opts.fetch(:base_path) do
97
+ config.fetch(:test_base_path) do |key|
98
+ raise UserError, "#{key} is not found in #{self}"
99
+ end
100
+ end
101
+
102
+ [
103
+ File.join(base, instance.suite.name, path),
104
+ File.join(base, path),
105
+ File.join(Dir.pwd, path)
106
+ ].find do |candidate|
107
+ type == :directory ? File.directory?(candidate) : File.file?(candidate)
108
+ end
109
+ end
110
+
111
+ # Returns an array of configuration keys.
112
+ #
113
+ # @return [Array] array of configuration keys
114
+ def config_keys
115
+ config.keys
116
+ end
117
+
118
+ # Returns a Hash of configuration and other useful diagnostic information.
119
+ #
120
+ # @return [Hash] a diagnostic hash
121
+ def diagnose
122
+ result = Hash.new
123
+ config_keys.sort.each { |k| result[k] = config[k] }
124
+ result
125
+ end
126
+
127
+ # Returns a Hash of configuration and other useful diagnostic information
128
+ # associated with the plugin itself (such as loaded version, class name,
129
+ # etc.).
130
+ #
131
+ # @return [Hash] a diagnostic hash
132
+ def diagnose_plugin
133
+ result = Hash.new
134
+ result[:name] = name
135
+ result.merge!(self.class.diagnose)
136
+ result
137
+ end
138
+
139
+ # Returns the name of this plugin, suitable for display in a CLI.
140
+ #
141
+ # @return [String] name of this plugin
142
+ def name
143
+ self.class.name.split("::").last
144
+ end
145
+
146
+ # @return [TrueClass,FalseClass] true if `:shell_type` is `"powershell"`
147
+ def powershell_shell?
148
+ ["powershell"].include?(instance.platform.shell_type)
149
+ end
150
+
151
+ # Builds a file path based on the `:os_type` (`"windows"` or `"unix"`).
152
+ #
153
+ # @return [String] joined path for instance's os_type
154
+ def remote_path_join(*parts)
155
+ path = File.join(*parts)
156
+ windows_os? ? path.gsub("/", "\\") : path.gsub("\\", "/")
157
+ end
158
+
159
+ # @return [TrueClass,FalseClass] true if `:os_type` is `"unix"` (or
160
+ # unset, for backwards compatibility)
161
+ def unix_os?
162
+ ["unix", nil].include?(instance.platform.os_type)
163
+ end
164
+
165
+ # Performs whatever tests that may be required to ensure that this plugin
166
+ # will be able to function in the current environment. This may involve
167
+ # checking for the presence of certain directories, software installed,
168
+ # etc.
169
+ #
170
+ # @raise [UserError] if the plugin will not be able to perform or if a
171
+ # documented dependency is missing from the system
172
+ def verify_dependencies
173
+ # this method may be left unimplemented if that is applicable
174
+ end
175
+
176
+ # @return [TrueClass,FalseClass] true if `:os_type` is `"windows"`
177
+ def windows_os?
178
+ ["windows"].include?(instance.platform.os_type)
179
+ end
180
+
181
+ private
182
+
183
+ # @return [LzayHash] a configuration hash
184
+ # @api private
185
+ attr_reader :config
186
+
187
+ # Initializes an internal configuration hash. The hash may contain
188
+ # callable blocks as values that are meant to be called lazily. This
189
+ # method is intended to be included in an object's .initialize method.
190
+ #
191
+ # @param config [Hash] initial provided configuration
192
+ # @api private
193
+ def init_config(config)
194
+ @config = LazyHash.new(config, self)
195
+ self.class.defaults.each do |attr, value|
196
+ @config[attr] = value unless @config.key?(attr)
197
+ end
198
+ end
199
+
200
+ # Expands file paths for certain configuration values. A configuration
201
+ # value is marked for file expansion with a expand_path_for declaration
202
+ # in the included class.
203
+ #
204
+ # @api private
205
+ def expand_paths!
206
+ root_path = config[:kitchen_root] || Dir.pwd
207
+ expanded_paths = LazyHash.new(self.class.expanded_paths, self).to_hash
208
+
209
+ expanded_paths.each do |key, should_expand|
210
+ next if !should_expand || config[key].nil? || config[key] == false
211
+
212
+ config[key] = if config[key].is_a?(Array)
213
+ config[key].map { |path| File.expand_path(path, root_path) }
214
+ else
215
+ File.expand_path(config[key], root_path)
216
+ end
217
+ end
218
+ end
219
+
220
+ # Loads any required third party Ruby libraries or runs any shell out
221
+ # commands to prepare the plugin. This method will be called in the
222
+ # context of the main thread of execution and so does not necessarily
223
+ # have to be thread safe.
224
+ #
225
+ # **Note:** any subclasses overriding this method would be well advised
226
+ # to call super when overriding this method, for example:
227
+ #
228
+ # @example overriding `#load_needed_dependencies!`
229
+ #
230
+ # class MyProvisioner < Kitchen::Provisioner::Base
231
+ # def load_needed_dependencies!
232
+ # super
233
+ # # any further work
234
+ # end
235
+ # end
236
+ #
237
+ # @raise [ClientError] if any library loading fails or any of the
238
+ # dependency requirements cannot be satisfied
239
+ # @api private
240
+ def load_needed_dependencies!
241
+ # this method may be left unimplemented if that is applicable
242
+ end
243
+
244
+ # @return [Logger] the instance's logger or Test Kitchen's common logger
245
+ # otherwise
246
+ # @api private
247
+ def logger
248
+ instance ? instance.logger : Kitchen.logger
249
+ end
250
+
251
+ # Builds a shell environment variable assignment string for the
252
+ # required shell type.
253
+ #
254
+ # @param name [String] variable name
255
+ # @param value [String] variable value
256
+ # @return [String] shell variable assignment
257
+ # @api private
258
+ def shell_env_var(name, value)
259
+ if powershell_shell?
260
+ shell_var("env:#{name}", value)
261
+ else
262
+ "#{shell_var(name, value)}; export #{name}"
263
+ end
264
+ end
265
+
266
+ # Builds a shell variable assignment string for the required shell type.
267
+ #
268
+ # @param name [String] variable name
269
+ # @param value [String] variable value
270
+ # @return [String] shell variable assignment
271
+ # @api private
272
+ def shell_var(name, value)
273
+ if powershell_shell?
274
+ %{$#{name} = "#{value}"}
275
+ else
276
+ %{#{name}="#{value}"}
277
+ end
278
+ end
279
+
280
+ # Runs all validations set up for the included class. Each validation is
281
+ # for a specific configuration attribute and has an associated callable
282
+ # block. Each validation block is called with the attribute, its value,
283
+ # and the included object for context.
284
+ #
285
+ # @api private
286
+ def validate_config!
287
+ self.class.validations.each do |attr, block|
288
+ block.call(attr, config[attr], self)
289
+ end
290
+ end
291
+
292
+ # Wraps a body of shell code with common context appropriate for the type
293
+ # of shell.
294
+ #
295
+ # @param code [String] the shell code to be wrapped
296
+ # @return [String] wrapped shell code
297
+ # @api private
298
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
299
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
300
+ def wrap_shell_code(code)
301
+ env = []
302
+ if config[:http_proxy] && !config[:http_proxy].empty?
303
+ env << shell_env_var("http_proxy", config[:http_proxy])
304
+ env << shell_env_var("HTTP_PROXY", config[:http_proxy])
305
+ else
306
+ export_proxy(env, "http")
307
+ end
308
+ if config[:https_proxy] && !config[:https_proxy].empty?
309
+ env << shell_env_var("https_proxy", config[:https_proxy])
310
+ env << shell_env_var("HTTPS_PROXY", config[:https_proxy])
311
+ else
312
+ export_proxy(env, "https")
313
+ end
314
+ if config[:ftp_proxy] && !config[:ftp_proxy].empty?
315
+ env << shell_env_var("ftp_proxy", config[:ftp_proxy])
316
+ env << shell_env_var("FTP_PROXY", config[:ftp_proxy])
317
+ else
318
+ export_proxy(env, "ftp")
319
+ end
320
+ # if http_proxy was set from environment variable or https_proxy was set
321
+ # from environment variable, or ftp_proxy was set from environment
322
+ # variable, include no_proxy environment variable, if set.
323
+ if (!config[:http_proxy] && (ENV["http_proxy"] || ENV["HTTP_PROXY"])) ||
324
+ (!config[:https_proxy] && (ENV["https_proxy"] || ENV["HTTPS_PROXY"])) ||
325
+ (!config[:ftp_proxy] && (ENV["ftp_proxy"] || ENV["FTP_PROXY"]))
326
+ env << shell_env_var("no_proxy", ENV["no_proxy"]) if ENV["no_proxy"]
327
+ env << shell_env_var("NO_PROXY", ENV["NO_PROXY"]) if ENV["NO_PROXY"]
328
+ end
329
+ if powershell_shell?
330
+ env.join("\n").concat("\n").concat(code)
331
+ else
332
+ Util.wrap_command(env.join("\n").concat("\n").concat(code))
333
+ end
334
+ end
335
+
336
+ # Helper method to export
337
+ #
338
+ # @param env [Array] the environment to modify
339
+ # @param code [String] the type of proxy to export, one of 'http', 'https' or 'ftp'
340
+ # @api private
341
+ def export_proxy(env, type)
342
+ env << shell_env_var("#{type}_proxy", ENV["#{type}_proxy"]) if ENV["#{type}_proxy"]
343
+ env << shell_env_var("#{type.upcase}_PROXY", ENV["#{type.upcase}_PROXY"]) if
344
+ ENV["#{type.upcase}_PROXY"]
345
+ end
346
+
347
+ # Class methods which will be mixed in on inclusion of Configurable module.
348
+ module ClassMethods
349
+
350
+ # Sets the loaded version of this plugin, usually corresponding to the
351
+ # RubyGems version of the plugin's library. If the plugin does not set
352
+ # this value, then `nil` will be used and reported.
353
+ #
354
+ # @example setting a version used by RubyGems
355
+ #
356
+ # require "kitchen/driver/vagrant_version"
357
+ #
358
+ # module Kitchen
359
+ # module Driver
360
+ # class Vagrant < Kitchen::Driver::Base
361
+ #
362
+ # plugin_version Kitchen::Driver::VAGRANT_VERSION
363
+ #
364
+ # end
365
+ # end
366
+ # end
367
+ #
368
+ # @param version [String] a version string
369
+ def plugin_version(version) # rubocop:disable Style/TrivialAccessors
370
+ @plugin_version = version
371
+ end
372
+
373
+ # Returns a Hash of configuration and other useful diagnostic
374
+ # information.
375
+ #
376
+ # @return [Hash] a diagnostic hash
377
+ def diagnose
378
+ {
379
+ :class => name,
380
+ :version => @plugin_version,
381
+ :api_version => @api_version
382
+ }
383
+ end
384
+
385
+ # Sets a sane default value for a configuration attribute. These values
386
+ # can be overridden by provided configuration or in a subclass with
387
+ # another default_config declaration.
388
+ #
389
+ # @example a nil default value
390
+ #
391
+ # default_config :i_am_nil
392
+ #
393
+ # @example a primitive default value
394
+ #
395
+ # default_config :use_sudo, true
396
+ #
397
+ # @example a block to compute a default value
398
+ #
399
+ # default_config :box_name do |subject|
400
+ # subject.instance.platform.name
401
+ # end
402
+ #
403
+ # @param attr [String] configuration attribute name
404
+ # @param value [Object, nil] static default value for attribute
405
+ # @yieldparam object [Object] a reference to the instantiated object
406
+ # @yieldreturn [Object, nil] dynamically computed value for the attribute
407
+ def default_config(attr, value = nil, &block)
408
+ defaults[attr] = block_given? ? block : value
409
+ end
410
+
411
+ # Ensures that an attribute which is a path will be fully expanded at
412
+ # the right time. This helps make the configuration unambiguous and much
413
+ # easier to debug and diagnose.
414
+ #
415
+ # Note that the file path expansion is only intended for paths on the
416
+ # local workstation invking the Test Kitchen code.
417
+ #
418
+ # @example the default usage
419
+ #
420
+ # expand_path_for :data_path
421
+ #
422
+ # @example disabling path expansion with a falsey value
423
+ #
424
+ # expand_path_for :relative_path, false
425
+ #
426
+ # @example using a block to determine whether or not to expand
427
+ #
428
+ # expand_path_for :relative_or_not_path do |subject|
429
+ # subject.instance.name =~ /default/
430
+ # end
431
+ #
432
+ # @param attr [String] configuration attribute name
433
+ # @param value [Object, nil] whether or not to exand the file path
434
+ # @yieldparam object [Object] a reference to the instantiated object
435
+ # @yieldreturn [Object, nil] dynamically compute whether or not to
436
+ # perform the file expansion
437
+ def expand_path_for(attr, value = true, &block)
438
+ expanded_paths[attr] = block_given? ? block : value
439
+ end
440
+
441
+ # Ensures that an attribute must have a non-nil, non-empty String value.
442
+ # The default behavior will be to raise a user error and thereby halting
443
+ # further configuration processing. Good use cases for require_config
444
+ # might be cloud provider credential keys and other similar data.
445
+ #
446
+ # @example a value that must not be nil or an empty String
447
+ #
448
+ # required_config :cloud_api_token
449
+ #
450
+ # @example using a block to use custom validation logic
451
+ #
452
+ # required_config :email do |attr, value, subject|
453
+ # raise UserError, "Must be an email address" unless value =~ /@/
454
+ # end
455
+ #
456
+ # @param attr [String] configuration attribute name
457
+ # @yieldparam attr [Symbol] the attribute name
458
+ # @yieldparam value [Object] the current value of the attribute
459
+ # @yieldparam object [Object] a reference to the instantiated object
460
+ def required_config(attr, &block)
461
+ if !block_given?
462
+ klass = self
463
+ block = lambda do |_, value, thing|
464
+ if value.nil? || value.to_s.empty?
465
+ attribute = "#{klass}#{thing.instance.to_str}#config[:#{attr}]"
466
+ raise UserError, "#{attribute} cannot be blank"
467
+ end
468
+ end
469
+ end
470
+ validations[attr] = block
471
+ end
472
+
473
+ # @return [Hash] a hash of attribute keys and default values which has
474
+ # been merged with any superclass defaults
475
+ # @api private
476
+ def defaults
477
+ @defaults ||= Hash.new.merge(super_defaults)
478
+ end
479
+
480
+ # @return [Hash] a hash of defaults from the included class' superclass
481
+ # if defined in the superclass, or an empty hash otherwise
482
+ # @api private
483
+ def super_defaults
484
+ if superclass.respond_to?(:defaults)
485
+ superclass.defaults
486
+ else
487
+ Hash.new
488
+ end
489
+ end
490
+
491
+ # @return [Hash] a hash of attribute keys and truthy/falsey values to
492
+ # determine if said attribute needs to be fully file path expanded,
493
+ # which has been merged with any superclass expanded paths
494
+ # @api private
495
+ def expanded_paths
496
+ @expanded_paths ||= Hash.new.merge(super_expanded_paths)
497
+ end
498
+
499
+ # @return [Hash] a hash of expanded paths from the included class'
500
+ # superclass if defined in the superclass, or an empty hash otherwise
501
+ # @api private
502
+ def super_expanded_paths
503
+ if superclass.respond_to?(:expanded_paths)
504
+ superclass.expanded_paths
505
+ else
506
+ Hash.new
507
+ end
508
+ end
509
+
510
+ # @return [Hash] a hash of attribute keys and valudation callable blocks
511
+ # which has been merged with any superclass valudations
512
+ # @api private
513
+ def validations
514
+ @validations ||= Hash.new.merge(super_validations)
515
+ end
516
+
517
+ # @return [Hash] a hash of validations from the included class'
518
+ # superclass if defined in the superclass, or an empty hash otherwise
519
+ # @api private
520
+ def super_validations
521
+ if superclass.respond_to?(:validations)
522
+ superclass.validations
523
+ else
524
+ Hash.new
525
+ end
526
+ end
527
+ end
528
+ end
529
+ end