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,705 @@
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 "fileutils" unless defined?(FileUtils)
19
+ require "pathname" unless defined?(Pathname)
20
+ require "json" unless defined?(JSON)
21
+ require "cgi" unless defined?(CGI)
22
+
23
+ require_relative "chef/policyfile"
24
+ require_relative "chef/berkshelf"
25
+ require_relative "chef/common_sandbox"
26
+ require_relative "../util"
27
+ module LicenseAcceptance
28
+ autoload :Acceptor, "license_acceptance/acceptor"
29
+ end
30
+
31
+ begin
32
+ require "chef-config/config"
33
+ require "chef-config/workstation_config_loader"
34
+ rescue LoadError # rubocop:disable Lint/HandleExceptions
35
+ # This space left intentionally blank.
36
+ end
37
+
38
+ module Kitchen
39
+ module Provisioner
40
+ # Common implementation details for Chef-related provisioners.
41
+ #
42
+ # @author Fletcher Nichol <fnichol@nichol.ca>
43
+ class ChefBase < Base
44
+ default_config :require_chef_omnibus, true
45
+ default_config :chef_omnibus_url, "https://omnitruck.chef.io/install.sh"
46
+ default_config :chef_omnibus_install_options, nil
47
+ default_config :chef_license, nil
48
+ default_config :run_list, []
49
+ default_config :policy_group, nil
50
+ default_config :attributes, {}
51
+ default_config :config_path, nil
52
+ default_config :log_file, nil
53
+ default_config :log_level do |provisioner|
54
+ provisioner[:debug] ? "debug" : "auto"
55
+ end
56
+ default_config :profile_ruby, false
57
+ # The older policyfile_zero used `policyfile` so support it for compat.
58
+ default_config :policyfile, nil
59
+ # Will try to autodetect by searching for `Policyfile.rb` if not set.
60
+ # If set, will error if the file doesn't exist.
61
+ default_config :policyfile_path, nil
62
+ # Will try to autodetect by searching for `Berksfile` if not set.
63
+ # If set, will error if the file doesn't exist.
64
+ default_config :berksfile_path, nil
65
+ # If set to true (which is the default from `chef generate`), try to update
66
+ # backend cookbook downloader on every kitchen run.
67
+ default_config :always_update_cookbooks, true
68
+ default_config :cookbook_files_glob, %w(
69
+ README.* VERSION metadata.{json,rb} attributes.rb recipe.rb
70
+ attributes/**/* definitions/**/* files/**/* libraries/**/*
71
+ providers/**/* recipes/**/* resources/**/* templates/**/*
72
+ ohai/**/*
73
+ ).join(",")
74
+ # to ease upgrades, allow the user to turn deprecation warnings into errors
75
+ default_config :deprecations_as_errors, false
76
+
77
+ # Override the default from Base so reboot handling works by default for Chef.
78
+ default_config :retry_on_exit_code, [35, 213]
79
+
80
+ default_config :multiple_converge, 1
81
+
82
+ default_config :enforce_idempotency, false
83
+
84
+ default_config :data_path do |provisioner|
85
+ provisioner.calculate_path("data")
86
+ end
87
+ expand_path_for :data_path
88
+
89
+ default_config :data_bags_path do |provisioner|
90
+ provisioner.calculate_path("data_bags")
91
+ end
92
+ expand_path_for :data_bags_path
93
+
94
+ default_config :environments_path do |provisioner|
95
+ provisioner.calculate_path("environments")
96
+ end
97
+ expand_path_for :environments_path
98
+
99
+ default_config :nodes_path do |provisioner|
100
+ provisioner.calculate_path("nodes")
101
+ end
102
+ expand_path_for :nodes_path
103
+
104
+ default_config :roles_path do |provisioner|
105
+ provisioner.calculate_path("roles")
106
+ end
107
+ expand_path_for :roles_path
108
+
109
+ default_config :clients_path do |provisioner|
110
+ provisioner.calculate_path("clients")
111
+ end
112
+ expand_path_for :clients_path
113
+
114
+ default_config :encrypted_data_bag_secret_key_path do |provisioner|
115
+ provisioner.calculate_path("encrypted_data_bag_secret_key", type: :file)
116
+ end
117
+ expand_path_for :encrypted_data_bag_secret_key_path
118
+
119
+ #
120
+ # New configuration options per RFC 091
121
+ # https://github.com/chef/chef-rfc/blob/master/rfc091-deprecate-kitchen-settings.md
122
+ #
123
+
124
+ # Setting product_name to nil. It is currently the pivot point
125
+ # between the two install paths (Mixlib::Install::ScriptGenerator and Mixlib::Install)
126
+ default_config :product_name
127
+
128
+ default_config :product_version, :latest
129
+
130
+ default_config :channel, :stable
131
+
132
+ default_config :install_strategy, "once"
133
+
134
+ default_config :platform
135
+
136
+ default_config :platform_version
137
+
138
+ default_config :architecture
139
+
140
+ default_config :download_url
141
+
142
+ default_config :checksum
143
+
144
+ deprecate_config_for :require_chef_omnibus do |provisioner|
145
+ case
146
+ when provisioner[:require_chef_omnibus] == false
147
+ Util.outdent!(<<-MSG)
148
+ The 'require_chef_omnibus' attribute with value of 'false' will
149
+ change to use the new 'install_strategy' attribute with a value of 'skip'.
150
+
151
+ Note: 'product_name' must be set in order to use 'install_strategy'.
152
+ Although this seems counterintuitive, it is necessary until
153
+ 'product_name' replaces 'require_chef_omnibus' as the default.
154
+
155
+ # New Usage #
156
+ provisioner:
157
+ product_name: <chef or chef-workstation>
158
+ install_strategy: skip
159
+ MSG
160
+ when provisioner[:require_chef_omnibus].to_s.match?(/\d/)
161
+ Util.outdent!(<<-MSG)
162
+ The 'require_chef_omnibus' attribute with version values will change
163
+ to use the new 'product_version' attribute.
164
+
165
+ Note: 'product_name' must be set in order to use 'product_version'
166
+ until 'product_name' replaces 'require_chef_omnibus' as the default.
167
+
168
+ # New Usage #
169
+ provisioner:
170
+ product_name: <chef or chef-workstation>
171
+ product_version: #{provisioner[:require_chef_omnibus]}
172
+ MSG
173
+ when provisioner[:require_chef_omnibus] == "latest"
174
+ Util.outdent!(<<-MSG)
175
+ The 'require_chef_omnibus' attribute with value of 'latest' will change
176
+ to use the new 'install_strategy' attribute with a value of 'always'.
177
+
178
+ Note: 'product_name' must be set in order to use 'install_strategy'
179
+ until 'product_name' replaces 'require_chef_omnibus' as the default.
180
+
181
+ # New Usage #
182
+ provisioner:
183
+ product_name: <chef or chef-workstation>
184
+ install_strategy: always
185
+ MSG
186
+ end
187
+ end
188
+
189
+ deprecate_config_for :chef_omnibus_url, Util.outdent!(<<-MSG)
190
+ Changing the 'chef_omnibus_url' attribute breaks existing functionality. It will
191
+ be removed in a future version.
192
+ MSG
193
+
194
+ deprecate_config_for :chef_omnibus_install_options, Util.outdent!(<<-MSG)
195
+ The 'chef_omnibus_install_options' attribute will be replaced by using
196
+ 'product_name' and 'channel' attributes.
197
+
198
+ Note: 'product_name' must be set in order to use 'channel'
199
+ until 'product_name' replaces 'require_chef_omnibus' as the default.
200
+
201
+ # Deprecated Example #
202
+ provisioner:
203
+ chef_omnibus_install_options: -P chef-workstation -c current
204
+
205
+ # New Usage #
206
+ provisioner:
207
+ product_name: chef-workstation
208
+ channel: current
209
+ MSG
210
+
211
+ deprecate_config_for :install_msi_url, Util.outdent!(<<-MSG)
212
+ The 'install_msi_url' will be relaced by the 'download_url' attribute.
213
+ 'download_url' will be applied to Bourne and Powershell download scripts.
214
+
215
+ Note: 'product_name' must be set in order to use 'download_url'
216
+ until 'product_name' replaces 'require_chef_omnibus' as the default.
217
+
218
+ # New Usage #
219
+ provisioner:
220
+ product_name: <chef or chef-workstation>
221
+ download_url: http://direct-download-url
222
+ MSG
223
+
224
+ deprecate_config_for :chef_metadata_url, Util.outdent!(<<-MSG)
225
+ The 'chef_metadata_url' will be removed. The Windows metadata URL will be
226
+ fully managed by using attribute settings.
227
+ MSG
228
+
229
+ # Reads the local Chef::Config object (if present). We do this because
230
+ # we want to start bring Chef config and Chef Workstation config closer
231
+ # together. For example, we want to configure proxy settings in 1
232
+ # location instead of 3 configuration files.
233
+ #
234
+ # @param config [Hash] initial provided configuration
235
+ def initialize(config = {})
236
+ super(config)
237
+
238
+ if defined?(ChefConfig::WorkstationConfigLoader)
239
+ ChefConfig::WorkstationConfigLoader.new(config[:config_path]).load
240
+ end
241
+ # This exports any proxy config present in the Chef config to
242
+ # appropriate environment variables, which Test Kitchen respects
243
+ ChefConfig::Config.export_proxies if defined?(ChefConfig::Config.export_proxies)
244
+ end
245
+
246
+ def doctor(state)
247
+ deprecated_config.each do |attr, msg|
248
+ info("**** #{attr} deprecated\n#{msg}")
249
+ end
250
+ end
251
+
252
+ # gives us the product version from either require_chef_omnibus or product_version
253
+ # If the non-default (true) value of require_chef_omnibus is present use that
254
+ # otherwise use config[:product_version] which defaults to :latest and is the actual
255
+ # default for chef provisioners
256
+ #
257
+ # @return [String,Symbol,NilClass] version or nil if not applicable
258
+ def product_version
259
+ case config[:require_chef_omnibus]
260
+ when FalseClass
261
+ nil
262
+ when TrueClass
263
+ config[:product_version]
264
+ else
265
+ config[:require_chef_omnibus]
266
+ end
267
+ end
268
+
269
+ # If the user has policyfiles we shell out to the `chef` executable, so need to ensure they have
270
+ # accepted the Chef Workstation license. Otherwise they just need the Chef Infra license.
271
+ #
272
+ # @return [String] license id to prompt for acceptance
273
+ def license_acceptance_id
274
+ case
275
+ when File.exist?(policyfile)
276
+ "chef-workstation"
277
+ when config[:product_name]
278
+ config[:product_name]
279
+ else
280
+ "chef"
281
+ end
282
+ end
283
+
284
+ # (see Base#check_license)
285
+ def check_license
286
+ name = license_acceptance_id
287
+ version = product_version
288
+ debug("Checking if we need to prompt for license acceptance on product: #{name} version: #{version}.")
289
+
290
+ acceptor = LicenseAcceptance::Acceptor.new(logger: Kitchen.logger, provided: config[:chef_license])
291
+ if acceptor.license_required?(name, version)
292
+ debug("License acceptance required for #{name} version: #{version}. Prompting")
293
+ license_id = acceptor.id_from_mixlib(name)
294
+ begin
295
+ acceptor.check_and_persist(license_id, version.to_s)
296
+ rescue LicenseAcceptance::LicenseNotAcceptedError => e
297
+ error("Cannot converge without accepting the #{e.product.pretty_name} License. Set it in your kitchen.yml or using the CHEF_LICENSE environment variable")
298
+ raise
299
+ end
300
+ config[:chef_license] ||= acceptor.acceptance_value
301
+ end
302
+ end
303
+
304
+ # (see Base#create_sandbox)
305
+ def create_sandbox
306
+ super
307
+ sanity_check_sandbox_options!
308
+ Chef::CommonSandbox.new(config, sandbox_path, instance).populate
309
+ end
310
+
311
+ # (see Base#init_command)
312
+ def init_command
313
+ dirs = %w{
314
+ cookbooks data data_bags environments roles clients
315
+ encrypted_data_bag_secret
316
+ }.sort.map { |dir| remote_path_join(config[:root_path], dir) }
317
+
318
+ vars = if powershell_shell?
319
+ init_command_vars_for_powershell(dirs)
320
+ else
321
+ init_command_vars_for_bourne(dirs)
322
+ end
323
+
324
+ prefix_command(shell_code_from_file(vars, "chef_base_init_command"))
325
+ end
326
+
327
+ # (see Base#install_command)
328
+ def install_command
329
+ return unless config[:require_chef_omnibus] || config[:product_name]
330
+ return if config[:product_name] && config[:install_strategy] == "skip"
331
+
332
+ prefix_command(install_script_contents)
333
+ end
334
+
335
+ private
336
+
337
+ def last_exit_code
338
+ "; exit $LastExitCode" if powershell_shell?
339
+ end
340
+
341
+ # @return [Hash] an option hash for the install commands
342
+ # @api private
343
+ def install_options
344
+ add_omnibus_directory_option if instance.driver.cache_directory
345
+ project = /\s*-P (\w+)\s*/.match(config[:chef_omnibus_install_options])
346
+ {
347
+ omnibus_url: config[:chef_omnibus_url],
348
+ project: project.nil? ? nil : project[1],
349
+ install_flags: config[:chef_omnibus_install_options],
350
+ sudo_command: sudo_command,
351
+ }.tap do |opts|
352
+ opts[:root] = config[:chef_omnibus_root] if config.key? :chef_omnibus_root
353
+ %i{install_msi_url http_proxy https_proxy}.each do |key|
354
+ opts[key] = config[key] if config.key? key
355
+ end
356
+ end
357
+ end
358
+
359
+ # Verify if the "omnibus_dir_option" has already been passed, if so we
360
+ # don't use the @driver.cache_directory
361
+ #
362
+ # @api private
363
+ def add_omnibus_directory_option
364
+ cache_dir_option = "#{omnibus_dir_option} #{instance.driver.cache_directory}"
365
+ if config[:chef_omnibus_install_options].nil?
366
+ config[:chef_omnibus_install_options] = cache_dir_option
367
+ elsif config[:chef_omnibus_install_options].match(/\s*#{omnibus_dir_option}\s*/).nil?
368
+ config[:chef_omnibus_install_options] << " " << cache_dir_option
369
+ end
370
+ end
371
+
372
+ # @return [String] an absolute path to a Policyfile, relative to the
373
+ # kitchen root
374
+ # @api private
375
+ def policyfile
376
+ policyfile_basename = config[:policyfile_path] || config[:policyfile] || "Policyfile.rb"
377
+ File.expand_path(policyfile_basename, config[:kitchen_root])
378
+ end
379
+
380
+ # @return [String] an absolute path to a Berksfile, relative to the
381
+ # kitchen root
382
+ # @api private
383
+ def berksfile
384
+ berksfile_basename = config[:berksfile_path] || config[:berksfile] || "Berksfile"
385
+ File.expand_path(berksfile_basename, config[:kitchen_root])
386
+ end
387
+
388
+ # Generates a Hash with default values for a solo.rb or client.rb Chef
389
+ # configuration file.
390
+ #
391
+ # @return [Hash] a configuration hash
392
+ # @api private
393
+ def default_config_rb # rubocop:disable Metrics/MethodLength
394
+ root = config[:root_path].gsub("$env:TEMP", "\#{ENV['TEMP']\}")
395
+
396
+ config_rb = {
397
+ node_name: instance.name,
398
+ checksum_path: remote_path_join(root, "checksums"),
399
+ file_cache_path: remote_path_join(root, "cache"),
400
+ file_backup_path: remote_path_join(root, "backup"),
401
+ cookbook_path: [
402
+ remote_path_join(root, "cookbooks"),
403
+ remote_path_join(root, "site-cookbooks"),
404
+ ],
405
+ data_bag_path: remote_path_join(root, "data_bags"),
406
+ environment_path: remote_path_join(root, "environments"),
407
+ node_path: remote_path_join(root, "nodes"),
408
+ role_path: remote_path_join(root, "roles"),
409
+ client_path: remote_path_join(root, "clients"),
410
+ user_path: remote_path_join(root, "users"),
411
+ validation_key: remote_path_join(root, "validation.pem"),
412
+ client_key: remote_path_join(root, "client.pem"),
413
+ chef_server_url: "http://127.0.0.1:8889",
414
+ encrypted_data_bag_secret: remote_path_join(
415
+ root, "encrypted_data_bag_secret"
416
+ ),
417
+ treat_deprecation_warnings_as_errors: config[:deprecations_as_errors],
418
+ }
419
+ config_rb[:chef_license] = config[:chef_license] unless config[:chef_license].nil?
420
+ config_rb
421
+ end
422
+
423
+ # Generates a rendered client.rb/solo.rb/knife.rb formatted file as a
424
+ # String.
425
+ #
426
+ # @param data [Hash] a key/value pair hash of configuration
427
+ # @return [String] a rendered Chef config file as a String
428
+ # @api private
429
+ def format_config_file(data)
430
+ data.each.map do |attr, value|
431
+ [attr, format_value(value)].join(" ")
432
+ end.join("\n")
433
+ end
434
+
435
+ # Converts a Ruby object to a String interpretation suitable for writing
436
+ # out to a client.rb/solo.rb/knife.rb file.
437
+ #
438
+ # @param obj [Object] an object
439
+ # @return [String] a string representation
440
+ # @api private
441
+ def format_value(obj)
442
+ if obj.is_a?(String) && obj =~ /^:/
443
+ obj
444
+ elsif obj.is_a?(String)
445
+ %{"#{obj.gsub(/\\/, "\\\\\\\\")}"}
446
+ elsif obj.is_a?(Array)
447
+ %{[#{obj.map { |i| format_value(i) }.join(", ")}]}
448
+ else
449
+ obj.inspect
450
+ end
451
+ end
452
+
453
+ # Generates the init command variables for Bourne shell-based platforms.
454
+ #
455
+ # @param dirs [Array<String>] directories
456
+ # @return [String] shell variable lines
457
+ # @api private
458
+ def init_command_vars_for_bourne(dirs)
459
+ [
460
+ shell_var("sudo_rm", sudo("rm")),
461
+ shell_var("dirs", dirs.join(" ")),
462
+ shell_var("root_path", config[:root_path]),
463
+ ].join("\n")
464
+ end
465
+
466
+ # Generates the init command variables for PowerShell-based platforms.
467
+ #
468
+ # @param dirs [Array<String>] directories
469
+ # @return [String] shell variable lines
470
+ # @api private
471
+ def init_command_vars_for_powershell(dirs)
472
+ [
473
+ %{$dirs = @(#{dirs.map { |d| %{"#{d}"} }.join(", ")})},
474
+ shell_var("root_path", config[:root_path]),
475
+ ].join("\n")
476
+ end
477
+
478
+ # Load cookbook dependency resolver code, if required.
479
+ #
480
+ # (see Base#load_needed_dependencies!)
481
+ def load_needed_dependencies!
482
+ super
483
+ if File.exist?(policyfile)
484
+ debug("Policyfile found at #{policyfile}, using Policyfile to resolve cookbook dependencies")
485
+ Chef::Policyfile.load!(logger: logger)
486
+ elsif File.exist?(berksfile)
487
+ debug("Berksfile found at #{berksfile}, using Berkshelf to resolve cookbook dependencies")
488
+ Chef::Berkshelf.load!(logger: logger)
489
+ end
490
+ end
491
+
492
+ # @return [String] contents of the install script
493
+ # @api private
494
+ def install_script_contents
495
+ # by default require_chef_omnibus is set to true. Check config[:product_name] first
496
+ # so that we can use it if configured.
497
+ if config[:product_name]
498
+ script_for_product
499
+ elsif config[:require_chef_omnibus]
500
+ script_for_omnibus_version
501
+ end
502
+ end
503
+
504
+ # @return [String] contents of product based install script
505
+ # @api private
506
+ def script_for_product
507
+ require "mixlib/install"
508
+ installer = Mixlib::Install.new({
509
+ product_name: config[:product_name],
510
+ product_version: config[:product_version],
511
+ channel: config[:channel].to_sym,
512
+ install_command_options: {
513
+ install_strategy: config[:install_strategy],
514
+ },
515
+ }.tap do |opts|
516
+ opts[:shell_type] = :ps1 if powershell_shell?
517
+ %i{platform platform_version architecture}.each do |key|
518
+ opts[key] = config[key] if config[key]
519
+ end
520
+
521
+ unless windows_os?
522
+ # omnitruck installer does not currently support a tmp dir option on windows
523
+ opts[:install_command_options][:tmp_dir] = config[:root_path]
524
+ opts[:install_command_options]["TMPDIR"] = config[:root_path]
525
+ end
526
+
527
+ if config[:download_url]
528
+ opts[:install_command_options][:download_url_override] = config[:download_url]
529
+ opts[:install_command_options][:checksum] = config[:checksum] if config[:checksum]
530
+ end
531
+
532
+ if instance.driver.cache_directory
533
+ download_dir_option = windows_os? ? :download_directory : :cmdline_dl_dir
534
+ opts[:install_command_options][download_dir_option] = instance.driver.cache_directory
535
+ end
536
+
537
+ proxies = {}.tap do |prox|
538
+ %i{http_proxy https_proxy ftp_proxy no_proxy}.each do |key|
539
+ prox[key] = config[key] if config[key]
540
+ end
541
+
542
+ # install.ps1 only supports http_proxy
543
+ prox.delete_if { |p| %i{https_proxy ftp_proxy no_proxy}.include?(p) } if powershell_shell?
544
+ end
545
+ opts[:install_command_options].merge!(proxies)
546
+ end)
547
+ config[:chef_omnibus_root] = installer.root
548
+ if powershell_shell?
549
+ installer.install_command
550
+ else
551
+ install_from_file(installer.install_command)
552
+ end
553
+ end
554
+
555
+ # @return [String] Correct option per platform to specify the the
556
+ # cache directory
557
+ # @api private
558
+ def omnibus_dir_option
559
+ windows_os? ? "-download_directory" : "-d"
560
+ end
561
+
562
+ def install_from_file(command)
563
+ install_file = "#{config[:root_path]}/chef-installer.sh"
564
+ script = []
565
+ script << "mkdir -p #{config[:root_path]}"
566
+ script << "if [ $? -ne 0 ]; then"
567
+ script << " echo Kitchen config setting root_path: '#{config[:root_path]}' not creatable by regular user "
568
+ script << " exit 1"
569
+ script << "fi"
570
+ script << "cat > #{install_file} <<\"EOL\""
571
+ script << command
572
+ script << "EOL"
573
+ script << "chmod +x #{install_file}"
574
+ script << sudo(install_file)
575
+ script.join("\n")
576
+ end
577
+
578
+ # @return [String] contents of version based install script
579
+ # @api private
580
+ def script_for_omnibus_version
581
+ require "mixlib/install/script_generator"
582
+ installer = Mixlib::Install::ScriptGenerator.new(
583
+ config[:require_chef_omnibus], powershell_shell?, install_options
584
+ )
585
+ config[:chef_omnibus_root] = installer.root
586
+ sudo(installer.install_command)
587
+ end
588
+
589
+ # Hook used in subclasses to indicate support for policyfiles.
590
+ #
591
+ # @abstract
592
+ # @return [Boolean]
593
+ # @api private
594
+ def supports_policyfile?
595
+ false
596
+ end
597
+
598
+ # @return [void]
599
+ # @raise [UserError]
600
+ # @api private
601
+ def sanity_check_sandbox_options!
602
+ if (config[:policyfile_path] || config[:policyfile]) && !File.exist?(policyfile)
603
+ raise UserError, "policyfile_path set in config "\
604
+ "(#{config[:policyfile_path]} could not be found. " \
605
+ "Expected to find it at full path #{policyfile}."
606
+ end
607
+ if config[:berksfile_path] && !File.exist?(berksfile)
608
+ raise UserError, "berksfile_path set in config "\
609
+ "(#{config[:berksfile_path]} could not be found. " \
610
+ "Expected to find it at full path #{berksfile}."
611
+ end
612
+ if File.exist?(policyfile) && !supports_policyfile?
613
+ raise UserError, "policyfile detected, but provisioner " \
614
+ "#{self.class.name} doesn't support Policyfiles. " \
615
+ "Either use a different provisioner, or delete/rename " \
616
+ "#{policyfile}."
617
+ end
618
+ end
619
+
620
+ # Writes a configuration file to the sandbox directory.
621
+ # @api private
622
+ def prepare_config_rb
623
+ data = default_config_rb.merge(config[config_filename.tr(".", "_").to_sym])
624
+ data = data.merge(named_run_list: config[:named_run_list]) if config[:named_run_list]
625
+
626
+ info("Preparing #{config_filename}")
627
+ debug("Creating #{config_filename} from #{data.inspect}")
628
+
629
+ File.open(File.join(sandbox_path, config_filename), "wb") do |file|
630
+ file.write(format_config_file(data))
631
+ end
632
+
633
+ prepare_config_idempotency_check(data) if config[:enforce_idempotency]
634
+ end
635
+
636
+ # Writes a configuration file to the sandbox directory
637
+ # to check for idempotency of the run.
638
+ # @api private
639
+ def prepare_config_idempotency_check(data)
640
+ handler_filename = "chef-client-fail-if-update-handler.rb"
641
+ source = File.join(
642
+ File.dirname(__FILE__), %w{.. .. .. support }, handler_filename
643
+ )
644
+ FileUtils.cp(source, File.join(sandbox_path, handler_filename))
645
+ File.open(File.join(sandbox_path, "client_no_updated_resources.rb"), "wb") do |file|
646
+ file.write(format_config_file(data))
647
+ file.write("\n\n")
648
+ file.write("handler_file = File.join(File.dirname(__FILE__), '#{handler_filename}')\n")
649
+ file.write "Chef::Config.from_file(handler_file)\n"
650
+ end
651
+ end
652
+
653
+ # Returns an Array of command line arguments for the chef client.
654
+ #
655
+ # @return [Array<String>] an array of command line arguments
656
+ # @api private
657
+ def chef_args(_config_filename)
658
+ raise "You must override in sub classes!"
659
+ end
660
+
661
+ # Returns a filename for the configuration file
662
+ # defaults to client.rb
663
+ #
664
+ # @return [String] a filename
665
+ # @api private
666
+ def config_filename
667
+ "client.rb"
668
+ end
669
+
670
+ # Gives the command used to run chef
671
+ # @api private
672
+ def chef_cmd(base_cmd)
673
+ if windows_os?
674
+ separator = [
675
+ "; if ($LastExitCode -ne 0) { ",
676
+ "throw \"Command failed with exit code $LastExitCode.\" } ;",
677
+ ].join
678
+ else
679
+ separator = " && "
680
+ end
681
+ chef_cmds(base_cmd).join(separator)
682
+ end
683
+
684
+ # Gives an array of command
685
+ # @api private
686
+ def chef_cmds(base_cmd)
687
+ cmd = prefix_command(wrap_shell_code(
688
+ [base_cmd, *chef_args(config_filename), last_exit_code].join(" ")
689
+ .tap { |str| str.insert(0, reload_ps1_path) if windows_os? }
690
+ ))
691
+
692
+ cmds = [cmd].cycle(config[:multiple_converge].to_i).to_a
693
+
694
+ if config[:enforce_idempotency]
695
+ idempotent_cmd = prefix_command(wrap_shell_code(
696
+ [base_cmd, *chef_args("client_no_updated_resources.rb"), last_exit_code].join(" ")
697
+ .tap { |str| str.insert(0, reload_ps1_path) if windows_os? }
698
+ ))
699
+ cmds[-1] = idempotent_cmd
700
+ end
701
+ cmds
702
+ end
703
+ end
704
+ end
705
+ end