kitchen-cinc 1.0.0

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.
@@ -0,0 +1,525 @@
1
+ #
2
+ # Copyright (C) 2013, Fletcher Nichol
3
+ # Copyright (C) 2026, Oregon State University
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require "fileutils" unless defined?(FileUtils)
18
+ require "pathname" unless defined?(Pathname)
19
+ require "json" unless defined?(JSON)
20
+ require "cgi" unless defined?(CGI)
21
+ require "kitchen/util"
22
+
23
+ require_relative "cinc/policyfile"
24
+ require_relative "cinc/berkshelf"
25
+ require_relative "cinc/common_sandbox"
26
+
27
+ begin
28
+ require "chef-config/config"
29
+ require "chef-config/workstation_config_loader"
30
+ rescue LoadError # rubocop:disable Lint/HandleExceptions
31
+ # This space left intentionally blank.
32
+ end
33
+
34
+ module Kitchen
35
+ module Provisioner
36
+ # Common implementation details for Cinc-related provisioners.
37
+ #
38
+ # @author Cinc Project
39
+ class CincBase < Base
40
+ default_config :run_list, []
41
+ default_config :policy_group, nil
42
+ default_config :attributes, {}
43
+ default_config :config_path, nil
44
+ default_config :log_file, nil
45
+ default_config :log_level do |provisioner|
46
+ provisioner[:debug] ? "debug" : "auto"
47
+ end
48
+ default_config :profile_ruby, false
49
+ # The older policyfile_zero used `policyfile` so support it for compat.
50
+ default_config :policyfile, nil
51
+ # Will try to autodetect by searching for `Policyfile.rb` if not set.
52
+ # If set, will error if the file doesn't exist.
53
+ default_config :policyfile_path, nil
54
+ # Will try to autodetect by searching for `Berksfile` if not set.
55
+ # If set, will error if the file doesn't exist.
56
+ default_config :berksfile_path, nil
57
+ # If set to true (which is the default from `chef generate`), try to update
58
+ # backend cookbook downloader on every kitchen run.
59
+ default_config :always_update_cookbooks, true
60
+ default_config :cookbook_files_glob, %w(
61
+ README.* VERSION metadata.{json,rb} attributes.rb recipe.rb
62
+ attributes/**/* definitions/**/* files/**/* libraries/**/*
63
+ providers/**/* recipes/**/* resources/**/* templates/**/*
64
+ ohai/**/* compliance/**/*
65
+ ).join(",")
66
+ # to ease upgrades, allow the user to turn deprecation warnings into errors
67
+ default_config :deprecations_as_errors, false
68
+
69
+ # Override the default from Base so reboot handling works by default for Cinc.
70
+ default_config :retry_on_exit_code, [35, 213]
71
+
72
+ default_config :multiple_converge, 1
73
+
74
+ default_config :enforce_idempotency, false
75
+
76
+ default_config :data_path do |provisioner|
77
+ provisioner.calculate_path("data")
78
+ end
79
+ expand_path_for :data_path
80
+
81
+ default_config :data_bags_path do |provisioner|
82
+ provisioner.calculate_path("data_bags")
83
+ end
84
+ expand_path_for :data_bags_path
85
+
86
+ default_config :environments_path do |provisioner|
87
+ provisioner.calculate_path("environments")
88
+ end
89
+ expand_path_for :environments_path
90
+
91
+ default_config :nodes_path do |provisioner|
92
+ provisioner.calculate_path("nodes")
93
+ end
94
+ expand_path_for :nodes_path
95
+
96
+ default_config :roles_path do |provisioner|
97
+ provisioner.calculate_path("roles")
98
+ end
99
+ expand_path_for :roles_path
100
+
101
+ default_config :clients_path do |provisioner|
102
+ provisioner.calculate_path("clients")
103
+ end
104
+ expand_path_for :clients_path
105
+
106
+ default_config :encrypted_data_bag_secret_key_path do |provisioner|
107
+ provisioner.calculate_path("encrypted_data_bag_secret_key", type: :file)
108
+ end
109
+ expand_path_for :encrypted_data_bag_secret_key_path
110
+
111
+ #
112
+ # Modern configuration options (RFC 091 equivalent for Cinc)
113
+ #
114
+
115
+ # Default product_name to "cinc" so that the modern Mixlib::Install
116
+ # code path is used automatically.
117
+ default_config :product_name, "cinc"
118
+
119
+ default_config :product_version, :latest
120
+
121
+ default_config :channel, :stable
122
+
123
+ default_config :install_strategy, "once"
124
+
125
+ default_config :platform
126
+
127
+ default_config :platform_version
128
+
129
+ default_config :architecture
130
+
131
+ default_config :download_url
132
+
133
+ default_config :checksum
134
+
135
+ # Reads the local Chef::Config object (if present). We do this because
136
+ # we want to start bringing Cinc config and Cinc Workstation config closer
137
+ # together. For example, we want to configure proxy settings in 1
138
+ # location instead of 3 configuration files.
139
+ #
140
+ # @param config [Hash] initial provided configuration
141
+ def initialize(config = {})
142
+ super(config)
143
+
144
+ if defined?(ChefConfig::WorkstationConfigLoader)
145
+ ChefConfig::WorkstationConfigLoader.new(config[:config_path]).load
146
+ end
147
+ # This exports any proxy config present in the config to
148
+ # appropriate environment variables, which Test Kitchen respects
149
+ ChefConfig::Config.export_proxies if defined?(ChefConfig::Config.export_proxies)
150
+ end
151
+
152
+ # (see Base#create_sandbox)
153
+ def create_sandbox
154
+ super
155
+ sanity_check_sandbox_options!
156
+ Cinc::CommonSandbox.new(config, sandbox_path, instance).populate
157
+ end
158
+
159
+ # (see Base#init_command)
160
+ def init_command
161
+ dirs = %w{
162
+ cookbooks data data_bags environments roles clients
163
+ encrypted_data_bag_secret
164
+ }.sort.map { |dir| remote_path_join(config[:root_path], dir) }
165
+
166
+ vars = if powershell_shell?
167
+ init_command_vars_for_powershell(dirs)
168
+ else
169
+ init_command_vars_for_bourne(dirs)
170
+ end
171
+
172
+ prefix_command(shell_code_from_file(vars, "cinc_base_init_command"))
173
+ end
174
+
175
+ # (see Base#install_command)
176
+ def install_command
177
+ return unless config[:product_name]
178
+ return if config[:install_strategy] == "skip"
179
+
180
+ prefix_command(install_script_contents)
181
+ end
182
+
183
+ private
184
+
185
+ def last_exit_code
186
+ "; exit $LastExitCode" if powershell_shell?
187
+ end
188
+
189
+ # @return [String] an absolute path to a Policyfile, relative to the
190
+ # kitchen root
191
+ # @api private
192
+ def policyfile
193
+ policyfile_basename = config[:policyfile_path] || config[:policyfile] || "Policyfile.rb"
194
+ File.expand_path(policyfile_basename, config[:kitchen_root])
195
+ end
196
+
197
+ # @return [String] an absolute path to a Berksfile, relative to the
198
+ # kitchen root
199
+ # @api private
200
+ def berksfile
201
+ berksfile_basename = config[:berksfile_path] || config[:berksfile] || "Berksfile"
202
+ File.expand_path(berksfile_basename, config[:kitchen_root])
203
+ end
204
+
205
+ # Generates a Hash with default values for a solo.rb or client.rb
206
+ # configuration file.
207
+ #
208
+ # @return [Hash] a configuration hash
209
+ # @api private
210
+ def default_config_rb
211
+ root = config[:root_path].gsub("$env:TEMP", "\#{ENV['TEMP']}")
212
+
213
+ {
214
+ node_name: instance.name,
215
+ checksum_path: remote_path_join(root, "checksums"),
216
+ file_cache_path: remote_path_join(root, "cache"),
217
+ file_backup_path: remote_path_join(root, "backup"),
218
+ cookbook_path: [
219
+ remote_path_join(root, "cookbooks"),
220
+ remote_path_join(root, "site-cookbooks"),
221
+ ],
222
+ data_bag_path: remote_path_join(root, "data_bags"),
223
+ environment_path: remote_path_join(root, "environments"),
224
+ node_path: remote_path_join(root, "nodes"),
225
+ role_path: remote_path_join(root, "roles"),
226
+ client_path: remote_path_join(root, "clients"),
227
+ user_path: remote_path_join(root, "users"),
228
+ validation_key: remote_path_join(root, "validation.pem"),
229
+ client_key: remote_path_join(root, "client.pem"),
230
+ chef_server_url: "http://127.0.0.1:8889",
231
+ encrypted_data_bag_secret: remote_path_join(
232
+ root, "encrypted_data_bag_secret"
233
+ ),
234
+ treat_deprecation_warnings_as_errors: config[:deprecations_as_errors],
235
+ }
236
+ end
237
+
238
+ # Generates a rendered client.rb/solo.rb formatted file as a
239
+ # String.
240
+ #
241
+ # @param data [Hash] a key/value pair hash of configuration
242
+ # @return [String] a rendered config file as a String
243
+ # @api private
244
+ def format_config_file(data)
245
+ data.each.map do |attr, value|
246
+ [attr, format_value(value)].join(" ")
247
+ end.join("\n")
248
+ end
249
+
250
+ # Converts a Ruby object to a String interpretation suitable for writing
251
+ # out to a client.rb/solo.rb file.
252
+ #
253
+ # @param obj [Object] an object
254
+ # @return [String] a string representation
255
+ # @api private
256
+ def format_value(obj)
257
+ if obj.is_a?(String) && obj =~ /^:/
258
+ obj
259
+ elsif obj.is_a?(String)
260
+ %{"#{obj.gsub(/["\\]/) { |m| "\\" + m }}"}
261
+ elsif obj.is_a?(Array)
262
+ %{[#{obj.map { |i| format_value(i) }.join(", ")}]}
263
+ else
264
+ obj.inspect
265
+ end
266
+ end
267
+
268
+ # Generates the init command variables for Bourne shell-based platforms.
269
+ #
270
+ # @param dirs [Array<String>] directories
271
+ # @return [String] shell variable lines
272
+ # @api private
273
+ def init_command_vars_for_bourne(dirs)
274
+ [
275
+ shell_var("sudo_rm", sudo("rm")),
276
+ shell_var("dirs", dirs.join(" ")),
277
+ shell_var("root_path", config[:root_path]),
278
+ ].join("\n")
279
+ end
280
+
281
+ # Generates the init command variables for PowerShell-based platforms.
282
+ #
283
+ # @param dirs [Array<String>] directories
284
+ # @return [String] shell variable lines
285
+ # @api private
286
+ def init_command_vars_for_powershell(dirs)
287
+ [
288
+ %{$dirs = @(#{dirs.map { |d| %{"#{d}"} }.join(", ")})},
289
+ shell_var("root_path", config[:root_path]),
290
+ ].join("\n")
291
+ end
292
+
293
+ # Load cookbook dependency resolver code, if required.
294
+ #
295
+ # (see Base#load_needed_dependencies!)
296
+ def load_needed_dependencies!
297
+ super
298
+ if File.exist?(policyfile)
299
+ debug("Policyfile found at #{policyfile}, using Policyfile to resolve cookbook dependencies")
300
+ Cinc::Policyfile.load!(logger:)
301
+ elsif File.exist?(berksfile)
302
+ debug("Berksfile found at #{berksfile}, using Berkshelf to resolve cookbook dependencies")
303
+ Cinc::Berkshelf.load!(logger:)
304
+ end
305
+ end
306
+
307
+ # @return [String] contents of the install script
308
+ # @api private
309
+ def install_script_contents
310
+ require "mixlib/install"
311
+ installer = Mixlib::Install.new({
312
+ product_name: config[:product_name],
313
+ product_version: config[:product_version],
314
+ channel: config[:channel].to_sym,
315
+ install_command_options: {
316
+ install_strategy: config[:install_strategy],
317
+ },
318
+ }.tap do |opts|
319
+ opts[:shell_type] = :ps1 if powershell_shell?
320
+ %i{platform platform_version architecture}.each do |key|
321
+ opts[key] = config[key] if config[key]
322
+ end
323
+
324
+ unless windows_os?
325
+ # omnitruck installer does not currently support a tmp dir option on windows
326
+ opts[:install_command_options][:tmp_dir] = config[:root_path]
327
+ opts[:install_command_options]["TMPDIR"] = config[:root_path]
328
+ end
329
+
330
+ if config[:download_url]
331
+ opts[:install_command_options][:download_url_override] = config[:download_url]
332
+ opts[:install_command_options][:checksum] = config[:checksum] if config[:checksum]
333
+ end
334
+
335
+ if instance.driver.cache_directory
336
+ download_dir_option = windows_os? ? :download_directory : :cmdline_dl_dir
337
+ opts[:install_command_options][download_dir_option] = instance.driver.cache_directory
338
+ end
339
+
340
+ proxies = {}.tap do |prox|
341
+ %i{http_proxy https_proxy ftp_proxy no_proxy}.each do |key|
342
+ prox[key] = config[key] if config[key]
343
+ end
344
+
345
+ # install.ps1 only supports http_proxy
346
+ prox.delete_if { |p| %i{https_proxy ftp_proxy no_proxy}.include?(p) } if powershell_shell?
347
+ end
348
+ opts[:install_command_options].merge!(proxies)
349
+ end)
350
+ config[:cinc_omnibus_root] = installer.root
351
+ if powershell_shell?
352
+ installer.install_command
353
+ else
354
+ install_from_file(installer.install_command)
355
+ end
356
+ end
357
+
358
+ def install_from_file(command)
359
+ install_file = "#{config[:root_path]}/cinc-installer.sh"
360
+ script = []
361
+ script << "mkdir -p #{config[:root_path]}"
362
+ script << "if [ $? -ne 0 ]; then"
363
+ script << " echo Kitchen config setting root_path: '#{config[:root_path]}' not creatable by regular user "
364
+ script << " exit 1"
365
+ script << "fi"
366
+ script << "cat > #{install_file} <<\"EOL\""
367
+ script << command
368
+ script << "EOL"
369
+ script << "chmod +x #{install_file}"
370
+ script << sudo(install_file)
371
+ script.join("\n")
372
+ end
373
+
374
+ # Hook used in subclasses to indicate support for policyfiles.
375
+ #
376
+ # @abstract
377
+ # @return [Boolean]
378
+ # @api private
379
+ def supports_policyfile?
380
+ false
381
+ end
382
+
383
+ # @return [void]
384
+ # @raise [UserError]
385
+ # @api private
386
+ def sanity_check_sandbox_options!
387
+ if (config[:policyfile_path] || config[:policyfile]) && !File.exist?(policyfile)
388
+ raise UserError, "policyfile_path set in config " \
389
+ "(#{config[:policyfile_path]} could not be found. " \
390
+ "Expected to find it at full path #{policyfile}."
391
+ end
392
+ if config[:berksfile_path] && !File.exist?(berksfile)
393
+ raise UserError, "berksfile_path set in config " \
394
+ "(#{config[:berksfile_path]} could not be found. " \
395
+ "Expected to find it at full path #{berksfile}."
396
+ end
397
+ if File.exist?(policyfile) && !supports_policyfile?
398
+ raise UserError, "policyfile detected, but provisioner " \
399
+ "#{self.class.name} doesn't support Policyfiles. " \
400
+ "Either use a different provisioner, or delete/rename " \
401
+ "#{policyfile}."
402
+ end
403
+ end
404
+
405
+ # Writes a configuration file to the sandbox directory.
406
+ # @api private
407
+ def prepare_config_rb
408
+ data = default_config_rb.merge(config[config_filename.tr(".", "_").to_sym])
409
+ data = data.merge(named_run_list: config[:named_run_list]) if config[:named_run_list]
410
+
411
+ info("Preparing #{config_filename}")
412
+ debug("Creating #{config_filename} from #{data.inspect}")
413
+
414
+ File.open(File.join(sandbox_path, config_filename), "wb") do |file|
415
+ file.write(format_config_file(data))
416
+ end
417
+
418
+ prepare_config_idempotency_check(data) if config[:enforce_idempotency]
419
+ end
420
+
421
+ # Writes a configuration file to the sandbox directory
422
+ # to check for idempotency of the run.
423
+ # @api private
424
+ def prepare_config_idempotency_check(data)
425
+ handler_filename = "cinc-client-fail-if-update-handler.rb"
426
+ source = File.join(
427
+ File.dirname(__FILE__), %w{.. .. .. support }, handler_filename
428
+ )
429
+ FileUtils.cp(source, File.join(sandbox_path, handler_filename))
430
+ File.open(File.join(sandbox_path, "client_no_updated_resources.rb"), "wb") do |file|
431
+ file.write(format_config_file(data))
432
+ file.write("\n\n")
433
+ file.write("handler_file = File.join(File.dirname(__FILE__), '#{handler_filename}')\n")
434
+ file.write "Chef::Config.from_file(handler_file)\n"
435
+ end
436
+ end
437
+
438
+ # Returns an Array of command line arguments for the cinc client.
439
+ #
440
+ # @return [Array<String>] an array of command line arguments
441
+ # @api private
442
+ def chef_args(_config_filename)
443
+ raise "You must override in sub classes!"
444
+ end
445
+
446
+ # Returns a filename for the configuration file
447
+ # defaults to client.rb
448
+ #
449
+ # @return [String] a filename
450
+ # @api private
451
+ def config_filename
452
+ "client.rb"
453
+ end
454
+
455
+ # Gives the command used to run cinc
456
+ # @api private
457
+ def chef_cmd(base_cmd)
458
+ if windows_os?
459
+ separator = [
460
+ "; if ($LastExitCode -ne 0) { ",
461
+ "throw \"Command failed with exit code $LastExitCode.\" } ;",
462
+ ].join
463
+ else
464
+ separator = " && "
465
+ end
466
+ chef_cmds(base_cmd).join(separator)
467
+ end
468
+
469
+ # Gives an array of commands
470
+ # @api private
471
+ def chef_cmds(base_cmd)
472
+ cmds = []
473
+ num_converges = config[:multiple_converge].to_i
474
+ idempotency = config[:enforce_idempotency]
475
+
476
+ # Execute Cinc Client n-1 times, without exiting
477
+ (num_converges - 1).times do
478
+ cmds << wrapped_chef_cmd(base_cmd, config_filename)
479
+ end
480
+
481
+ # Append another execution with Windows specific Exit code helper or (for
482
+ # idempotency check) a specific config file which assures no changed resources.
483
+ cmds << unless idempotency
484
+ wrapped_chef_cmd(base_cmd, config_filename, append: last_exit_code)
485
+ else
486
+ wrapped_chef_cmd(base_cmd, "client_no_updated_resources.rb", append: last_exit_code)
487
+ end
488
+ cmds
489
+ end
490
+
491
+ # Concatenate all arguments and wrap it with shell-specifics
492
+ # @api private
493
+ def wrapped_chef_cmd(base_cmd, configfile, append: "")
494
+ args = []
495
+
496
+ args << base_cmd
497
+ args << chef_args(configfile)
498
+ args << append
499
+
500
+ shell_cmd = args.flatten.join(" ")
501
+ shell_cmd = shell_cmd.prepend(reload_ps1_path) if windows_os?
502
+
503
+ prefix_command(wrap_shell_code(shell_cmd))
504
+ end
505
+
506
+ # Builds a complete command given a variables String preamble and a file
507
+ # containing shell code.
508
+ #
509
+ # @param vars [String] shell variables, as a String
510
+ # @param file [String] file basename (without extension) containing
511
+ # shell code
512
+ # @return [String] command
513
+ # @api private
514
+ def shell_code_from_file(vars, file)
515
+ src_file = File.join(
516
+ File.dirname(__FILE__),
517
+ %w{.. .. .. support},
518
+ file + (powershell_shell? ? ".ps1" : ".sh")
519
+ )
520
+
521
+ wrap_shell_code([vars, "", File.read(src_file)].join("\n"))
522
+ end
523
+ end
524
+ end
525
+ end
@@ -0,0 +1,164 @@
1
+ #
2
+ # Copyright (C) 2013, Fletcher Nichol
3
+ # Copyright (C) 2026, Oregon State University
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # https://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+
17
+ require_relative "cinc_base"
18
+
19
+ module Kitchen
20
+ module Provisioner
21
+ # Cinc Infra provisioner using cinc-client in local mode.
22
+ #
23
+ # @author Cinc Project
24
+ class CincInfra < CincBase
25
+ kitchen_provisioner_api_version 2
26
+
27
+ plugin_version Kitchen::VERSION
28
+
29
+ default_config :client_rb, {}
30
+ default_config :named_run_list, {}
31
+ default_config :json_attributes, true
32
+ default_config :cinc_zero_host, nil
33
+ default_config :cinc_zero_port, 8889
34
+
35
+ default_config :cinc_client_path do |provisioner|
36
+ provisioner
37
+ .remote_path_join(%W{#{provisioner[:cinc_omnibus_root]} bin cinc-client})
38
+ .tap { |path| path.concat(".bat") if provisioner.windows_os? }
39
+ end
40
+
41
+ default_config :ruby_bindir do |provisioner|
42
+ provisioner
43
+ .remote_path_join(%W{#{provisioner[:cinc_omnibus_root]} embedded bin})
44
+ end
45
+
46
+ # (see Base#create_sandbox)
47
+ def create_sandbox
48
+ super
49
+ prepare_validation_pem
50
+ prepare_config_rb
51
+ end
52
+
53
+ def run_command
54
+ cmd = "#{sudo(config[:cinc_client_path])} --local-mode".tap { |str| str.insert(0, "& ") if powershell_shell? }
55
+
56
+ chef_cmd(cmd)
57
+ end
58
+
59
+ private
60
+
61
+ # Adds optional flags to a cinc-client command, depending on
62
+ # configuration data. Note that this method mutates the incoming Array.
63
+ #
64
+ # @param args [Array<String>] array of flags
65
+ # @api private
66
+ def add_optional_chef_client_args!(args)
67
+ if config[:json_attributes]
68
+ json = remote_path_join(config[:root_path], "dna.json")
69
+ args << "--json-attributes #{json}"
70
+ end
71
+
72
+ args << "--logfile #{config[:log_file]}" if config[:log_file]
73
+
74
+ # these flags are cinc-client local mode only and will not work
75
+ # on older versions of cinc-client
76
+ if config[:cinc_zero_host]
77
+ args << "--chef-zero-host #{config[:cinc_zero_host]}"
78
+ end
79
+
80
+ if config[:cinc_zero_port]
81
+ args << "--chef-zero-port #{config[:cinc_zero_port]}"
82
+ end
83
+
84
+ args << "--profile-ruby" if config[:profile_ruby]
85
+
86
+ if config[:slow_resource_report]
87
+ if config[:slow_resource_report].is_a?(Integer)
88
+ args << "--slow-report #{config[:slow_resource_report]}"
89
+ else
90
+ args << "--slow-report"
91
+ end
92
+ end
93
+ end
94
+
95
+ # Returns an Array of command line arguments for the cinc client.
96
+ #
97
+ # @return [Array<String>] an array of command line arguments
98
+ # @api private
99
+ def chef_args(client_rb_filename)
100
+ level = config[:log_level]
101
+ args = [
102
+ "--config #{remote_path_join(config[:root_path], client_rb_filename)}",
103
+ "--log_level #{level}",
104
+ "--force-formatter",
105
+ "--no-color",
106
+ ]
107
+ add_optional_chef_client_args!(args)
108
+
109
+ args
110
+ end
111
+
112
+ # Generates a string of shell environment variables needed for the
113
+ # cinc-client-zero.rb shim script to properly function.
114
+ #
115
+ # @return [String] a shell script string
116
+ # @api private
117
+ def chef_client_zero_env
118
+ root = config[:root_path]
119
+ gem_home = gem_path = remote_path_join(root, "chef-client-zero-gems")
120
+ gem_cache = remote_path_join(gem_home, "cache")
121
+
122
+ [
123
+ shell_env_var("CHEF_REPO_PATH", root),
124
+ shell_env_var("GEM_HOME", gem_home),
125
+ shell_env_var("GEM_PATH", gem_path),
126
+ shell_env_var("GEM_CACHE", gem_cache),
127
+ ].join("\n").concat("\n")
128
+ end
129
+
130
+ # Writes a fake (but valid) validation.pem into the sandbox directory.
131
+ #
132
+ # @api private
133
+ def prepare_validation_pem
134
+ info("Preparing validation.pem")
135
+ debug("Using a dummy validation.pem")
136
+
137
+ source = File.join(File.dirname(__FILE__),
138
+ %w{.. .. .. support dummy-validation.pem})
139
+ FileUtils.cp(source, File.join(sandbox_path, "validation.pem"))
140
+ end
141
+
142
+ # Returns the command that will run a backwards compatible shim script
143
+ # that approximates local mode in a modern cinc-client run.
144
+ #
145
+ # @return [String] the command string
146
+ # @api private
147
+ def shim_command
148
+ ruby = remote_path_join(config[:ruby_bindir], "ruby")
149
+ .tap { |path| path.concat(".exe") if windows_os? }
150
+ shim = remote_path_join(config[:root_path], "chef-client-zero.rb")
151
+
152
+ "#{chef_client_zero_env}\n#{sudo(ruby)} #{shim}"
153
+ end
154
+
155
+ # This provisioner supports policyfiles, so override the default (which
156
+ # is false)
157
+ # @return [true] always returns true
158
+ # @api private
159
+ def supports_policyfile?
160
+ true
161
+ end
162
+ end
163
+ end
164
+ end