test-kitchen-rsync 3.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +21 -0
  3. data/LICENSE +15 -0
  4. data/Rakefile +53 -0
  5. data/bin/zl-kitchen +11 -0
  6. data/lib/kitchen/base64_stream.rb +48 -0
  7. data/lib/kitchen/chef_utils_wiring.rb +40 -0
  8. data/lib/kitchen/cli.rb +413 -0
  9. data/lib/kitchen/collection.rb +52 -0
  10. data/lib/kitchen/color.rb +63 -0
  11. data/lib/kitchen/command/action.rb +41 -0
  12. data/lib/kitchen/command/console.rb +54 -0
  13. data/lib/kitchen/command/diagnose.rb +84 -0
  14. data/lib/kitchen/command/doctor.rb +39 -0
  15. data/lib/kitchen/command/exec.rb +37 -0
  16. data/lib/kitchen/command/list.rb +148 -0
  17. data/lib/kitchen/command/login.rb +39 -0
  18. data/lib/kitchen/command/package.rb +32 -0
  19. data/lib/kitchen/command/sink.rb +50 -0
  20. data/lib/kitchen/command/test.rb +47 -0
  21. data/lib/kitchen/command.rb +207 -0
  22. data/lib/kitchen/config.rb +344 -0
  23. data/lib/kitchen/configurable.rb +616 -0
  24. data/lib/kitchen/data_munger.rb +1024 -0
  25. data/lib/kitchen/diagnostic.rb +138 -0
  26. data/lib/kitchen/driver/base.rb +133 -0
  27. data/lib/kitchen/driver/dummy.rb +105 -0
  28. data/lib/kitchen/driver/exec.rb +70 -0
  29. data/lib/kitchen/driver/proxy.rb +70 -0
  30. data/lib/kitchen/driver/ssh_base.rb +351 -0
  31. data/lib/kitchen/driver.rb +40 -0
  32. data/lib/kitchen/errors.rb +243 -0
  33. data/lib/kitchen/generator/init.rb +254 -0
  34. data/lib/kitchen/instance.rb +726 -0
  35. data/lib/kitchen/lazy_hash.rb +148 -0
  36. data/lib/kitchen/lifecycle_hook/base.rb +78 -0
  37. data/lib/kitchen/lifecycle_hook/local.rb +53 -0
  38. data/lib/kitchen/lifecycle_hook/remote.rb +39 -0
  39. data/lib/kitchen/lifecycle_hooks.rb +92 -0
  40. data/lib/kitchen/loader/yaml.rb +377 -0
  41. data/lib/kitchen/logger.rb +422 -0
  42. data/lib/kitchen/logging.rb +52 -0
  43. data/lib/kitchen/login_command.rb +49 -0
  44. data/lib/kitchen/metadata_chopper.rb +49 -0
  45. data/lib/kitchen/platform.rb +64 -0
  46. data/lib/kitchen/plugin.rb +76 -0
  47. data/lib/kitchen/plugin_base.rb +60 -0
  48. data/lib/kitchen/provisioner/base.rb +269 -0
  49. data/lib/kitchen/provisioner/chef/berkshelf.rb +116 -0
  50. data/lib/kitchen/provisioner/chef/common_sandbox.rb +350 -0
  51. data/lib/kitchen/provisioner/chef/policyfile.rb +163 -0
  52. data/lib/kitchen/provisioner/chef_apply.rb +121 -0
  53. data/lib/kitchen/provisioner/chef_base.rb +705 -0
  54. data/lib/kitchen/provisioner/chef_infra.rb +167 -0
  55. data/lib/kitchen/provisioner/chef_solo.rb +82 -0
  56. data/lib/kitchen/provisioner/chef_zero.rb +12 -0
  57. data/lib/kitchen/provisioner/dummy.rb +75 -0
  58. data/lib/kitchen/provisioner/shell.rb +157 -0
  59. data/lib/kitchen/provisioner.rb +42 -0
  60. data/lib/kitchen/rake_tasks.rb +80 -0
  61. data/lib/kitchen/shell_out.rb +90 -0
  62. data/lib/kitchen/ssh.rb +289 -0
  63. data/lib/kitchen/state_file.rb +112 -0
  64. data/lib/kitchen/suite.rb +48 -0
  65. data/lib/kitchen/thor_tasks.rb +63 -0
  66. data/lib/kitchen/transport/base.rb +236 -0
  67. data/lib/kitchen/transport/dummy.rb +78 -0
  68. data/lib/kitchen/transport/exec.rb +145 -0
  69. data/lib/kitchen/transport/ssh.rb +579 -0
  70. data/lib/kitchen/transport/winrm.rb +546 -0
  71. data/lib/kitchen/transport.rb +40 -0
  72. data/lib/kitchen/util.rb +229 -0
  73. data/lib/kitchen/verifier/base.rb +243 -0
  74. data/lib/kitchen/verifier/busser.rb +275 -0
  75. data/lib/kitchen/verifier/dummy.rb +75 -0
  76. data/lib/kitchen/verifier/shell.rb +99 -0
  77. data/lib/kitchen/verifier.rb +39 -0
  78. data/lib/kitchen/version.rb +20 -0
  79. data/lib/kitchen/which.rb +26 -0
  80. data/lib/kitchen.rb +152 -0
  81. data/lib/vendor/hash_recursive_merge.rb +79 -0
  82. data/support/busser_install_command.ps1 +14 -0
  83. data/support/busser_install_command.sh +21 -0
  84. data/support/chef-client-fail-if-update-handler.rb +15 -0
  85. data/support/chef_base_init_command.ps1 +18 -0
  86. data/support/chef_base_init_command.sh +1 -0
  87. data/support/chef_base_install_command.ps1 +85 -0
  88. data/support/chef_base_install_command.sh +229 -0
  89. data/support/download_helpers.sh +109 -0
  90. data/support/dummy-validation.pem +27 -0
  91. data/templates/driver/CHANGELOG.md.erb +3 -0
  92. data/templates/driver/Gemfile.erb +3 -0
  93. data/templates/driver/README.md.erb +64 -0
  94. data/templates/driver/Rakefile.erb +21 -0
  95. data/templates/driver/driver.rb.erb +23 -0
  96. data/templates/driver/gemspec.erb +29 -0
  97. data/templates/driver/gitignore.erb +17 -0
  98. data/templates/driver/license_apachev2.erb +15 -0
  99. data/templates/driver/license_lgplv3.erb +16 -0
  100. data/templates/driver/license_mit.erb +22 -0
  101. data/templates/driver/license_reserved.erb +5 -0
  102. data/templates/driver/tailor.erb +4 -0
  103. data/templates/driver/travis.yml.erb +11 -0
  104. data/templates/driver/version.rb.erb +12 -0
  105. data/templates/init/chefignore.erb +2 -0
  106. data/templates/init/kitchen.yml.erb +18 -0
  107. data/test-kitchen.gemspec +52 -0
  108. metadata +528 -0
@@ -0,0 +1,377 @@
1
+ #
2
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
+ #
4
+ # Copyright (C) 2013, Fletcher Nichol
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+ require "erb" unless defined?(Erb)
19
+ require_relative "../../vendor/hash_recursive_merge"
20
+ require "psych" unless defined?(Psych)
21
+ require "yaml" unless defined?(YAML)
22
+
23
+ module Kitchen
24
+ module Loader
25
+ # YAML file loader for Test Kitchen configuration. This class is
26
+ # responisble for parsing the main YAML file and the local YAML if it
27
+ # exists. Local file configuration will win over the default configuration.
28
+ # The client of this class should not require any YAML loading or parsing
29
+ # logic.
30
+ #
31
+ # @author Fletcher Nichol <fnichol@nichol.ca>
32
+ class YAML
33
+ # Creates a new loader that can parse and load YAML files.
34
+ #
35
+ # @param options [Hash] configuration for a new loader
36
+ # @option options [String] :project_config path to the Kitchen
37
+ # config YAML file (default: `./kitchen.yml`)
38
+ # @option options [String] :local_config path to the Kitchen local
39
+ # config YAML file (default: `./kitchen.local.yml`)
40
+ # @option options [String] :global_config path to the Kitchen global
41
+ # config YAML file (default: `$HOME/.kitchen/config.yml`)
42
+ # @option options [String] :process_erb whether or not to process YAML
43
+ # through an ERB processor (default: `true`)
44
+ # @option options [String] :process_local whether or not to process a
45
+ # local kitchen YAML file, if it exists (default: `true`)
46
+ def initialize(options = {})
47
+ @config_file =
48
+ File.expand_path(options[:project_config] || default_config_file)
49
+ @local_config_file =
50
+ File.expand_path(options[:local_config] || default_local_config_file)
51
+ @global_config_file =
52
+ File.expand_path(options[:global_config] || default_global_config_file)
53
+
54
+ @process_erb = options.fetch(:process_erb, true)
55
+ @process_local = options.fetch(:process_local, true)
56
+ @process_global = options.fetch(:process_global, true)
57
+ end
58
+
59
+ # Reads, parses, and merges YAML configuration files and returns a Hash
60
+ # of tne merged data.
61
+ #
62
+ # @return [Hash] merged configuration data
63
+ def read
64
+ unless File.exist?(config_file)
65
+ raise UserError, "Kitchen YAML file #{config_file} does not exist."
66
+ end
67
+
68
+ Util.symbolized_hash(combined_hash)
69
+ end
70
+
71
+ # Returns a Hash of configuration and other useful diagnostic information.
72
+ #
73
+ # @return [Hash] a diagnostic hash
74
+ def diagnose
75
+ result = {}
76
+ result[:process_erb] = @process_erb
77
+ result[:process_local] = @process_local
78
+ result[:process_global] = @process_global
79
+ result[:global_config] = diagnose_component(:global_yaml, global_config_file)
80
+ result[:project_config] = diagnose_component(:yaml, config_file)
81
+ result[:local_config] = diagnose_component(:local_yaml, local_config_file)
82
+ result[:combined_config] = diagnose_component(:combined_hash)
83
+ result
84
+ end
85
+
86
+ private
87
+
88
+ # @return [String] the absolute path to the Kitchen config YAML file
89
+ # @api private
90
+ attr_reader :config_file
91
+
92
+ # @return [String] the absolute path to the Kitchen local config YAML
93
+ # file
94
+ # @api private
95
+ attr_reader :local_config_file
96
+
97
+ # @return [String] the absolute path to the Kitchen global config YAML
98
+ # file
99
+ # @api private
100
+ attr_reader :global_config_file
101
+
102
+ # Performed a prioritized recursive merge of several source Hashes and
103
+ # returns a new merged Hash. There are 3 sources of configuration data:
104
+ #
105
+ # 1. local config
106
+ # 2. project config
107
+ # 3. global config
108
+ #
109
+ # The merge order is local -> project -> global, meaning that elements at
110
+ # the top of the above list will be merged last, and have greater
111
+ # precedence than elements at the bottom of the list.
112
+ #
113
+ # @return [Hash] a new merged Hash
114
+ # @api private
115
+ def combined_hash
116
+ y = if @process_global
117
+ normalize(global_yaml).rmerge(normalize(yaml))
118
+ else
119
+ normalize(yaml)
120
+ end
121
+ @process_local ? y.rmerge(normalize(local_yaml)) : y
122
+ end
123
+
124
+ # Loads and returns the Kitchen config YAML as a Hash.
125
+ #
126
+ # @return [Hash] the config hash
127
+ # @api private
128
+ def yaml
129
+ parse_yaml_string(yaml_string(config_file), config_file)
130
+ end
131
+
132
+ # Loads and returns the Kitchen local config YAML as a Hash.
133
+ #
134
+ # @return [Hash] the config hash
135
+ # @api private
136
+ def local_yaml
137
+ parse_yaml_string(yaml_string(local_config_file), local_config_file)
138
+ end
139
+
140
+ # Loads and returns the Kitchen global config YAML as a Hash.
141
+ #
142
+ # @return [Hash] the config hash
143
+ # @api private
144
+ def global_yaml
145
+ parse_yaml_string(yaml_string(global_config_file), global_config_file)
146
+ end
147
+
148
+ # Loads a file to a string and optionally passes it through an ERb
149
+ # process.
150
+ #
151
+ # @return [String] a file's contents as a string
152
+ # @api private
153
+ def yaml_string(file)
154
+ string = read_file(file)
155
+
156
+ @process_erb ? process_erb(string, file) : string
157
+ end
158
+
159
+ # Passes a string through ERb to evaulate any ERb blocks.
160
+ #
161
+ # @param string [String] the string to process
162
+ # @param file [String] an absolute path to the file represented as the
163
+ # passed in string, used for error reporting
164
+ # @return [String] a new string, passed through an ERb process
165
+ # @raise [UserError] if an ERb parsing error occurs
166
+ # @api private
167
+ def process_erb(string, file)
168
+ tpl = ERB.new(string)
169
+ tpl.filename = file
170
+ tpl.result
171
+ rescue => e
172
+ raise UserError, "Error parsing ERB content in #{file} " \
173
+ "(#{e.class}: #{e.message}).\n" \
174
+ "Please run `kitchen diagnose --no-instances --loader' to help " \
175
+ "debug your issue."
176
+ end
177
+
178
+ # Reads a file and returns its contents as a string.
179
+ #
180
+ # @param file [String] a path to a file
181
+ # @return [String] the files contents, or an empty string if the file
182
+ # does not exist
183
+ # @api private
184
+ def read_file(file)
185
+ File.exist?(file.to_s) ? IO.read(file) : ""
186
+ end
187
+
188
+ # Determines the default absolute path to the Kitchen config YAML file,
189
+ # based on current working directory. We prefer `kitchen.yml` to the
190
+ # older `.kitchen.yml`.
191
+ #
192
+ # @return [String] an absolute path to a Kitchen config YAML file
193
+ # @api private
194
+ def default_config_file
195
+ if File.exist?(kitchen_yml) && File.exist?(dot_kitchen_yml)
196
+ raise UserError, "Both #{kitchen_yml} and #{dot_kitchen_yml} found. Please use the un-dotted variant: #{kitchen_yml}."
197
+ end
198
+
199
+ if !File.exist?(kitchen_yml) && !File.exist?(dot_kitchen_yml)
200
+ return kitchen_yml
201
+ end
202
+
203
+ File.exist?(kitchen_yml) ? kitchen_yml : dot_kitchen_yml
204
+ end
205
+
206
+ # The absolute path to an un-hidden Kitchen config YAML file.
207
+ def kitchen_yml
208
+ File.join(Dir.pwd, "kitchen.yml")
209
+ end
210
+
211
+ # The absolute path to an hidden Kitchen config YAML file.
212
+ def dot_kitchen_yml
213
+ File.join(Dir.pwd, ".kitchen.yml")
214
+ end
215
+
216
+ # Determines the default absolute path to the Kitchen local YAML file,
217
+ # based on the base Kitchen config YAML file.
218
+
219
+ # @return [String] an absolute path to a Kitchen local YAML file
220
+ # @raise [UserError] if both dotted and undotted versions of the default
221
+ # local YAML file exist, e.g. both kitchen.local.yml and .kitchen.local.yml
222
+ # @api private
223
+ def default_local_config_file
224
+ config_dir, default_local_config = File.split(config_file.sub(/(#{File.extname(config_file)})$/, '.local\1'))
225
+
226
+ undot_config = default_local_config.sub(/^\./, "")
227
+ dot_config = ".#{undot_config}"
228
+
229
+ if File.exist?(File.join(config_dir, undot_config)) && File.exist?(File.join(config_dir, dot_config))
230
+ raise UserError, "Both #{undot_config} and #{dot_config} found in #{config_dir}. Please use #{default_local_config} which matches your #{config_file}."
231
+ end
232
+
233
+ File.exist?(File.join(config_dir, dot_config)) ? File.join(config_dir, dot_config) : File.join(config_dir, undot_config)
234
+ end
235
+
236
+ # Determines the default absolute path to the Kitchen global YAML file,
237
+ # based on the base Kitchen config YAML file.
238
+ #
239
+ # @return [String] an absolute path to a Kitchen global YAML file
240
+ # @api private
241
+ def default_global_config_file
242
+ File.join(File.expand_path(ENV["HOME"]), ".kitchen", "config.yml")
243
+ end
244
+
245
+ # Generate a diganose Hash for a particular YAML file Hash. If an error
246
+ # occurs when loading the data, then a failure hash will be inserted
247
+ # into the `:raw_data` sub-hash.
248
+ #
249
+ # @param component [Symbol] a YAML source component
250
+ # @param file [String] the absolute path to a file which is used for
251
+ # reporting (default: `nil`)
252
+ # @return [Hash] a hash data structure
253
+ # @api private
254
+ def diagnose_component(component, file = nil)
255
+ return if file && !File.exist?(file)
256
+
257
+ hash = begin
258
+ send(component)
259
+ rescue => e
260
+ failure_hash(e, file)
261
+ end
262
+
263
+ { filename: file, raw_data: hash }
264
+ end
265
+
266
+ # Generates a Hash respresenting a failure, given an Exception object.
267
+ #
268
+ # @param e [Exception] an exception
269
+ # @param file [String] the absolute path to a file (default: `nil`)
270
+ # @return [Hash] a hash data structure
271
+ # @api private
272
+ def failure_hash(e, file = nil)
273
+ result = {
274
+ error: {
275
+ exception: e.inspect,
276
+ message: e.message,
277
+ backtrace: e.backtrace,
278
+ },
279
+ }
280
+ result[:error][:raw_file] = IO.read(file) unless file.nil?
281
+ result
282
+ end
283
+
284
+ # Destructively modify an object containing one or more hashes so that
285
+ # the resulting formatted data can be consumed upstream.
286
+ #
287
+ # @param obj [Object] an object
288
+ # @return [Object] an object
289
+ # @api private
290
+ def normalize(obj)
291
+ if obj.is_a?(Hash)
292
+ obj.inject({}) { |h, (k, v)| normalize_hash(h, k, v); h }
293
+ else
294
+ obj
295
+ end
296
+ end
297
+
298
+ # Normalizes certain keys in the root of a data hash to be a proper
299
+ # sub-hash in all cases. Specifically handled are the following cases:
300
+ #
301
+ # * If the value for certain keys (`"driver"`, `"provisioner"`,
302
+ # `"busser"`) are set to `nil`, a new Hash will be put in its place.
303
+ # * If the value for certain keys is a String, then the value is
304
+ # converted to a new Hash with a default key pointing to the original
305
+ # String.
306
+ #
307
+ # Given a hash:
308
+ #
309
+ # { "driver" => nil }
310
+ #
311
+ # this method would return:
312
+ #
313
+ # { "driver" => {} }
314
+ #
315
+ # Given a hash:
316
+ #
317
+ # { :driver => "coolbeans" }
318
+ #
319
+ # this method would return:
320
+ #
321
+ # { :name => { "driver" => "coolbeans" } }
322
+ #
323
+ #
324
+ # @param hash [Hash] the Hash to normalize
325
+ # @param key [Symbol] the key to normalize
326
+ # @param value [Object] the value to normalize
327
+ # @api private
328
+ def normalize_hash(hash, key, value)
329
+ case key
330
+ when "driver", "provisioner", "busser"
331
+ hash[key] = if value.nil?
332
+ {}
333
+ elsif value.is_a?(String)
334
+ default_key = key == "busser" ? "version" : "name"
335
+ { default_key => value }
336
+ else
337
+ normalize(value)
338
+ end
339
+ else
340
+ hash[key] = normalize(value)
341
+ end
342
+ end
343
+
344
+ # Parses a YAML string and returns a Hash.
345
+ #
346
+ # @param string [String] a yaml document as a string
347
+ # @param file_name [String] an absolute path to the file represented as
348
+ # the passed in string, used for error reporting
349
+ # @return [Hash] a hash
350
+ # @raise [UserError] if the string document cannot be parsed
351
+ # @api private
352
+ def parse_yaml_string(string, file_name)
353
+ return {} if string.nil? || string.empty?
354
+
355
+ result =
356
+ if Gem::Requirement.new(">= 3.1.0").satisfied_by?(Gem::Version.new(Psych::VERSION))
357
+ # ruby >= 2.6.0
358
+ ::YAML.safe_load(string, permitted_classes: [Symbol], permitted_symbols: [], aliases: true) || {}
359
+ else
360
+ # ruby < 2.6.0
361
+ ::YAML.safe_load(string, [Symbol], [], true) || {}
362
+ end
363
+ unless result.is_a?(Hash)
364
+ raise UserError, "Error parsing #{file_name} as YAML " \
365
+ "(Result of parse was not a Hash, but was a #{result.class}).\n" \
366
+ "Please run `kitchen diagnose --no-instances --loader' to help " \
367
+ "debug your issue."
368
+ end
369
+ result
370
+ rescue SyntaxError, Psych::SyntaxError, Psych::DisallowedClass
371
+ raise UserError, "Error parsing #{file_name} as YAML.\n" \
372
+ "Please run `kitchen diagnose --no-instances --loader' to help " \
373
+ "debug your issue."
374
+ end
375
+ end
376
+ end
377
+ end