test-kitchen-rsync 3.0.0.pre.1

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 (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