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,350 @@
1
+ #
2
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
+ #
4
+ # Copyright (C) 2015, 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 "json" unless defined?(JSON)
19
+
20
+ module Kitchen
21
+ module Provisioner
22
+ module Chef
23
+ # Internal object to manage common sandbox preparation for
24
+ # Chef-related provisioners.
25
+ #
26
+ # @author Fletcher Nichol <fnichol@nichol.ca>
27
+ # @api private
28
+ class CommonSandbox
29
+ include Logging
30
+
31
+ # Constructs a new object, taking config, a sandbox path, and an
32
+ # instance.
33
+ #
34
+ # @param config [Hash] configuration hash
35
+ # @param sandbox_path [String] path to local sandbox directory
36
+ # @param instance [Instance] an instance
37
+ def initialize(config, sandbox_path, instance)
38
+ @config = config
39
+ @sandbox_path = sandbox_path
40
+ @instance = instance
41
+ end
42
+
43
+ # Populate the sandbox.
44
+ def populate
45
+ prepare_json
46
+ prepare_cache
47
+ prepare_cookbooks
48
+ prepare(:data)
49
+ prepare(:data_bags)
50
+ prepare(:environments)
51
+ prepare(:nodes)
52
+ prepare(:roles)
53
+ prepare(:clients)
54
+ prepare(
55
+ :secret,
56
+ type: :file,
57
+ dest_name: "encrypted_data_bag_secret",
58
+ key_name: :encrypted_data_bag_secret_key_path
59
+ )
60
+ end
61
+
62
+ private
63
+
64
+ # @return [Hash] configuration hash
65
+ # @api private
66
+ attr_reader :config
67
+
68
+ # @return [Instance] an instance
69
+ # @api private
70
+ attr_reader :instance
71
+
72
+ # @return [String] path to local sandbox directory
73
+ # @api private
74
+ attr_reader :sandbox_path
75
+
76
+ # @return [String] name of the policy_group, nil results in "local"
77
+ # @api private
78
+ attr_reader :policy_group
79
+
80
+ # Generates a list of all files in the cookbooks directory in the
81
+ # sandbox path.
82
+ #
83
+ # @return [Array<String>] an array of absolute paths to files
84
+ # @api private
85
+ def all_files_in_cookbooks
86
+ Util.list_directory(tmpbooks_dir, include_dot: true, recurse: true)
87
+ .select { |fn| File.file?(fn) }
88
+ end
89
+
90
+ # @return [String] an absolute path to a Policyfile, relative to the
91
+ # kitchen root
92
+ # @api private
93
+ def policyfile
94
+ basename = config[:policyfile_path] || config[:policyfile] || "Policyfile.rb"
95
+ File.expand_path(basename, config[:kitchen_root])
96
+ end
97
+
98
+ # @return [String] an absolute path to a Berksfile, relative to the
99
+ # kitchen root
100
+ # @api private
101
+ def berksfile
102
+ basename = config[:berksfile_path] || "Berksfile"
103
+ File.expand_path(basename, config[:kitchen_root])
104
+ end
105
+
106
+ # @return [String] an absolute path to a cookbooks/ directory, relative
107
+ # to the kitchen root
108
+ # @api private
109
+ def cookbooks_dir
110
+ File.join(config[:kitchen_root], "cookbooks")
111
+ end
112
+
113
+ # Copies a cookbooks/ directory into the sandbox path.
114
+ #
115
+ # @api private
116
+ def cp_cookbooks
117
+ info("Preparing cookbooks from project directory")
118
+ debug("Using cookbooks from #{cookbooks_dir}")
119
+
120
+ FileUtils.mkdir_p(tmpbooks_dir)
121
+ FileUtils.cp_r(File.join(cookbooks_dir, "."), tmpbooks_dir)
122
+
123
+ cp_site_cookbooks if File.directory?(site_cookbooks_dir)
124
+ cp_this_cookbook if File.exist?(metadata_rb)
125
+ end
126
+
127
+ # Copies a site-cookbooks/ directory into the sandbox path.
128
+ #
129
+ # @api private
130
+ def cp_site_cookbooks
131
+ info("Preparing site-cookbooks from project directory")
132
+ debug("Using cookbooks from #{site_cookbooks_dir}")
133
+
134
+ FileUtils.mkdir_p(tmpsitebooks_dir)
135
+ FileUtils.cp_r(File.join(site_cookbooks_dir, "."), tmpsitebooks_dir)
136
+ end
137
+
138
+ # Copies the current project, assumed to be a Chef cookbook into the
139
+ # sandbox path.
140
+ #
141
+ # @api private
142
+ def cp_this_cookbook
143
+ info("Preparing current project directory as a cookbook")
144
+ debug("Using metadata.rb from #{metadata_rb}")
145
+
146
+ cb_name = MetadataChopper.extract(metadata_rb).first || raise(UserError,
147
+ "The metadata.rb does not define the 'name' key." \
148
+ " Please add: `name '<cookbook_name>'` to metadata.rb and retry")
149
+
150
+ cb_path = File.join(tmpbooks_dir, cb_name)
151
+
152
+ glob = Util.list_directory(config[:kitchen_root])
153
+
154
+ FileUtils.mkdir_p(cb_path)
155
+ FileUtils.cp_r(glob, cb_path)
156
+ end
157
+
158
+ # Removes all non-cookbook files in the sandbox path.
159
+ #
160
+ # @api private
161
+ def filter_only_cookbook_files
162
+ info("Removing non-cookbook files before transfer")
163
+ FileUtils.rm(all_files_in_cookbooks - only_cookbook_files)
164
+ Util.list_directory(tmpbooks_dir, recurse: true)
165
+ .reverse_each { |fn| FileUtils.rmdir(fn) if File.directory?(fn) && Dir.entries(fn).size == 2 }
166
+ end
167
+
168
+ # @return [Logger] the instance's logger or Test Kitchen's common
169
+ # logger otherwise
170
+ # @api private
171
+ def logger
172
+ instance ? instance.logger : Kitchen.logger
173
+ end
174
+
175
+ # Creates a minimal, no-op cookbook in the sandbox path.
176
+ #
177
+ # @api private
178
+ def make_fake_cookbook
179
+ info("Policyfile, Berksfile, cookbooks/, or metadata.rb not found " \
180
+ "so Chef Infra Client will run, but do nothing. Is this intended?")
181
+ name = File.basename(config[:kitchen_root])
182
+ fake_cb = File.join(tmpbooks_dir, name)
183
+ FileUtils.mkdir_p(fake_cb)
184
+ File.open(File.join(fake_cb, "metadata.rb"), "wb") do |file|
185
+ file.write(%{name "#{name}"\n})
186
+ end
187
+ end
188
+
189
+ # @return [String] an absolute path to a metadata.rb, relative to the
190
+ # kitchen root
191
+ # @api private
192
+ def metadata_rb
193
+ File.join(config[:kitchen_root], "metadata.rb")
194
+ end
195
+
196
+ # Generates a list of all typical cookbook files needed in a Chef run,
197
+ # located in the cookbooks directory in the sandbox path.
198
+ #
199
+ # @return [Array<String>] an array of absolute paths to files
200
+ # @api private
201
+ def only_cookbook_files
202
+ glob = File.join("*", "{#{config[:cookbook_files_glob]}}")
203
+ Util.safe_glob(tmpbooks_dir, glob, File::FNM_DOTMATCH)
204
+ .select { |fn| File.file?(fn) && ! %w{. ..}.include?(fn) }
205
+ end
206
+
207
+ # Prepares a generic Chef component source directory or file for
208
+ # inclusion in the sandbox path. These components might includes nodes,
209
+ # roles, etc.
210
+ #
211
+ # @param component [Symbol,String] a component name such as `:node`
212
+ # @param opts [Hash] optional configuration
213
+ # @option opts [Symbol] :type whether the component is a directory or
214
+ # file (default: `:directory`)
215
+ # @option opts [Symbol] :key_name the key name in the config hash from
216
+ # which to pull the source path (default: `"#{component}_path"`)
217
+ # @option opts [String] :dest_name the destination file or directory
218
+ # basename in the sandbox path (default: `component.to_s`)
219
+ # @api private
220
+ def prepare(component, opts = {})
221
+ opts = { type: :directory }.merge(opts)
222
+ key_name = opts.fetch(:key_name, "#{component}_path")
223
+ src = config[key_name.to_sym]
224
+ return if src.nil?
225
+
226
+ info("Preparing #{component}")
227
+ debug("Using #{component} from #{src}")
228
+
229
+ dest = File.join(sandbox_path, opts.fetch(:dest_name, component.to_s))
230
+
231
+ case opts[:type]
232
+ when :directory
233
+ FileUtils.mkdir_p(dest)
234
+ Array(src).each { |dir| FileUtils.cp_r(Util.list_directory(dir), dest) }
235
+ when :file
236
+ FileUtils.mkdir_p(File.dirname(dest))
237
+ Array(src).each { |file| FileUtils.cp_r(file, dest) }
238
+ end
239
+ end
240
+
241
+ # Prepares a cache directory for inclusion in the sandbox path.
242
+ #
243
+ # @api private
244
+ def prepare_cache
245
+ FileUtils.mkdir_p(File.join(sandbox_path, "cache"))
246
+ end
247
+
248
+ # Prepares Chef cookbooks for inclusion in the sandbox path.
249
+ #
250
+ # @api private
251
+ # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
252
+ def prepare_cookbooks
253
+ if File.exist?(policyfile)
254
+ resolve_with_policyfile
255
+ elsif File.exist?(berksfile)
256
+ resolve_with_berkshelf
257
+ elsif File.directory?(cookbooks_dir)
258
+ cp_cookbooks
259
+ elsif File.exist?(metadata_rb)
260
+ cp_this_cookbook
261
+ else
262
+ make_fake_cookbook
263
+ end
264
+
265
+ filter_only_cookbook_files
266
+ end
267
+
268
+ # Prepares a Chef JSON file, sometimes called a dna.json or
269
+ # first-boot.json, for inclusion in the sandbox path.
270
+ #
271
+ # @api private
272
+ def prepare_json
273
+ dna = if File.exist?(policyfile)
274
+ update_dna_for_policyfile
275
+ else
276
+ config[:attributes].merge(run_list: config[:run_list])
277
+ end
278
+
279
+ info("Preparing dna.json")
280
+ debug("Creating dna.json from #{dna.inspect}")
281
+
282
+ File.open(File.join(sandbox_path, "dna.json"), "wb") do |file|
283
+ file.write(dna.to_json)
284
+ end
285
+ end
286
+
287
+ def update_dna_for_policyfile
288
+ policy = Chef::Policyfile.new(
289
+ policyfile, sandbox_path,
290
+ logger: logger,
291
+ always_update: config[:always_update_cookbooks],
292
+ policy_group: policy_group
293
+ )
294
+ Kitchen.mutex.synchronize do
295
+ policy.compile
296
+ end
297
+ policy_name = JSON.parse(IO.read(policy.lockfile))["name"]
298
+ policy_group = config[:policy_group] || "local"
299
+ config[:attributes].merge(policy_name: policy_name, policy_group: policy_group)
300
+ end
301
+
302
+ # Performs a Policyfile cookbook resolution inside a common mutex.
303
+ #
304
+ # @api private
305
+ def resolve_with_policyfile
306
+ Kitchen.mutex.synchronize do
307
+ Chef::Policyfile.new(
308
+ policyfile, sandbox_path,
309
+ logger: logger,
310
+ always_update: config[:always_update_cookbooks],
311
+ policy_group: config[:policy_group]
312
+ ).resolve
313
+ end
314
+ end
315
+
316
+ # Performs a Berkshelf cookbook resolution inside a common mutex.
317
+ #
318
+ # @api private
319
+ def resolve_with_berkshelf
320
+ Kitchen.mutex.synchronize do
321
+ Chef::Berkshelf.new(berksfile, tmpbooks_dir,
322
+ logger: logger,
323
+ always_update: config[:always_update_cookbooks]).resolve
324
+ end
325
+ end
326
+
327
+ # @return [String] an absolute path to a site-cookbooks/ directory,
328
+ # relative to the kitchen root
329
+ # @api private
330
+ def site_cookbooks_dir
331
+ File.join(config[:kitchen_root], "site-cookbooks")
332
+ end
333
+
334
+ # @return [String] an absolute path to a cookbooks/ directory in the
335
+ # sandbox path
336
+ # @api private
337
+ def tmpbooks_dir
338
+ File.join(sandbox_path, "cookbooks")
339
+ end
340
+
341
+ # @return [String] an absolute path to a site cookbooks directory in the
342
+ # sandbox path
343
+ # @api private
344
+ def tmpsitebooks_dir
345
+ File.join(sandbox_path, "cookbooks")
346
+ end
347
+ end
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,163 @@
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 "shellwords" unless defined?(Shellwords)
19
+ require "rbconfig" unless defined?(RbConfig)
20
+
21
+ require_relative "../../errors"
22
+ require_relative "../../logging"
23
+ require_relative "../../shell_out"
24
+ require_relative "../../which"
25
+
26
+ module Kitchen
27
+ module Provisioner
28
+ module Chef
29
+ # Chef cookbook resolver that uses Policyfiles to calculate dependencies.
30
+ #
31
+ # @author Fletcher Nichol <fnichol@nichol.ca>
32
+ class Policyfile
33
+ include Logging
34
+ include ShellOut
35
+ include Which
36
+
37
+ # Creates a new cookbook resolver.
38
+ #
39
+ # @param policyfile [String] path to a Policyfile
40
+ # @param path [String] path in which to vendor the resulting
41
+ # cookbooks
42
+ # @param logger [Kitchen::Logger] a logger to use for output, defaults
43
+ # to `Kitchen.logger`
44
+ def initialize(policyfile, path, logger: Kitchen.logger, always_update: false, policy_group: nil)
45
+ @policyfile = policyfile
46
+ @path = path
47
+ @logger = logger
48
+ @always_update = always_update
49
+ @policy_group = policy_group
50
+ end
51
+
52
+ # Loads the library code required to use the resolver.
53
+ #
54
+ # @param logger [Kitchen::Logger] a logger to use for output, defaults
55
+ # to `Kitchen.logger`
56
+ def self.load!(logger: Kitchen.logger)
57
+ # intentionally left blank
58
+ end
59
+
60
+ # Performs the cookbook resolution and vendors the resulting cookbooks
61
+ # in the desired path.
62
+ def resolve
63
+ if policy_group
64
+ info("Exporting cookbook dependencies from Policyfile #{path} with policy_group #{policy_group} using `#{cli_path} export`...")
65
+ run_command("#{cli_path} export #{escape_path(policyfile)} #{escape_path(path)} --policy_group #{policy_group} --force")
66
+ else
67
+ info("Exporting cookbook dependencies from Policyfile #{path} using `#{cli_path} export`...")
68
+ run_command("#{cli_path} export #{escape_path(policyfile)} #{escape_path(path)} --force")
69
+ end
70
+ end
71
+
72
+ # Runs `chef install` to determine the correct cookbook set and
73
+ # generate the policyfile lock.
74
+ def compile
75
+ if File.exist?(lockfile)
76
+ info("Installing cookbooks for Policyfile #{policyfile} using `#{cli_path} install`")
77
+ else
78
+ info("Policy lock file doesn't exist, running `#{cli_path} install` for Policyfile #{policyfile}...")
79
+ end
80
+ run_command("#{cli_path} install #{escape_path(policyfile)}")
81
+
82
+ if always_update
83
+ info("Updating policy lock using `#{cli_path} update`")
84
+ run_command("#{cli_path} update #{escape_path(policyfile)}")
85
+ end
86
+ end
87
+
88
+ # Return the path to the lockfile corresponding to this policyfile.
89
+ #
90
+ # @return [String]
91
+ def lockfile
92
+ policyfile.gsub(/\.rb\Z/, ".lock.json")
93
+ end
94
+
95
+ private
96
+
97
+ # @return [String] path to a Policyfile
98
+ # @api private
99
+ attr_reader :policyfile
100
+
101
+ # @return [String] path in which to vendor the resulting cookbooks
102
+ # @api private
103
+ attr_reader :path
104
+
105
+ # @return [Kitchen::Logger] a logger to use for output
106
+ # @api private
107
+ attr_reader :logger
108
+
109
+ # @return [Boolean] If true, always update cookbooks in the policy.
110
+ # @api private
111
+ attr_reader :always_update
112
+
113
+ # @return [String] name of the policy_group, nil results in "local"
114
+ # @api private
115
+ attr_reader :policy_group
116
+
117
+ # Escape spaces in a path in way that works with both Sh (Unix) and
118
+ # Windows.
119
+ #
120
+ # @param path [String] Path to escape
121
+ # @return [String]
122
+ # @api private
123
+ def escape_path(path)
124
+ if /mswin|mingw/.match?(RbConfig::CONFIG["host_os"])
125
+ # I know what you're thinking: "just use Shellwords.escape". That
126
+ # method produces incorrect results on Windows with certain input
127
+ # which would be a metacharacter in Sh but is not for one or more of
128
+ # Windows command line parsing libraries. This covers the 99% case of
129
+ # spaces in the path without breaking other stuff.
130
+ if /[ \t\n\v"]/.match?(path)
131
+ "\"#{path.gsub(/[ \t\n\v\"\\]/) { |m| "\\" + m[0] }}\""
132
+ else
133
+ path
134
+ end
135
+ else
136
+ Shellwords.escape(path)
137
+ end
138
+ end
139
+
140
+ # Find the `chef` or `chef-cli` commands in the path or raise `chef` is present in
141
+ # ChefDK / Workstation releases, but is no longer shipped in any gems now that we
142
+ # use a Go based wrapper for the `chef` command in Workstation. The Ruby CLI has been
143
+ # renamed `chef-cli` under the hood and is shipped in the `chef-cli` gem.
144
+ #
145
+ # @api private
146
+ # @returns [String]
147
+ def cli_path
148
+ @cli_path ||= which("chef-cli") || which("chef") || no_cli_found_error
149
+ end
150
+
151
+ # @api private
152
+ def no_cli_found_error
153
+ @logger.fatal("The `chef` or `chef-cli` executables cannot be found in your " \
154
+ "PATH. Ensure you have installed Chef Workstation " \
155
+ "from https://downloads.chef.io and that your PATH " \
156
+ "setting includes the path to the `chef` or `chef-cli` commands.")
157
+ raise UserError, "Could not find the chef or chef-cli executables in your PATH."
158
+ end
159
+
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,121 @@
1
+ #
2
+ # Author:: SAWANOBORI Yukihiko <sawanoboriyu@higanworks.com>)
3
+ #
4
+ # Copyright (C) 2015, HiganWorks LLC
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
+ # Usage:
19
+ #
20
+ # puts your recipes to` apply/` directory.
21
+ #
22
+ # An example of .kitchen.yml.
23
+ #
24
+ # ---
25
+ # driver:
26
+ # name: vagrant
27
+ #
28
+ # provisioner:
29
+ # name: chef_apply
30
+ #
31
+ # platforms:
32
+ # - name: ubuntu-20.04
33
+ # - name: centos-8
34
+ #
35
+ # suites:
36
+ # - name: default
37
+ # run_list:
38
+ # - recipe1
39
+ # - recipe2
40
+ #
41
+ #
42
+ # The chef-apply runs twice below.
43
+ #
44
+ # chef-apply apply/recipe1.rb
45
+ # chef-apply apply/recipe2.rb
46
+
47
+ require_relative "chef_base"
48
+
49
+ module Kitchen
50
+ module Provisioner
51
+ # Chef Apply provisioner.
52
+ #
53
+ # @author SAWANOBORI Yukihiko <sawanoboriyu@higanworks.com>)
54
+ class ChefApply < ChefBase
55
+ kitchen_provisioner_api_version 2
56
+
57
+ plugin_version Kitchen::VERSION
58
+
59
+ default_config :chef_apply_path do |provisioner|
60
+ provisioner
61
+ .remote_path_join(%W{#{provisioner[:chef_omnibus_root]} bin chef-apply})
62
+ .tap { |path| path.concat(".bat") if provisioner.windows_os? }
63
+ end
64
+
65
+ default_config :apply_path do |provisioner|
66
+ provisioner.calculate_path("apply")
67
+ end
68
+ expand_path_for :apply_path
69
+
70
+ # (see ChefBase#create_sandbox)
71
+ def create_sandbox
72
+ @sandbox_path = Dir.mktmpdir("#{instance.name}-sandbox-")
73
+ File.chmod(0755, sandbox_path)
74
+ info("Preparing files for transfer")
75
+ debug("Creating local sandbox in #{sandbox_path}")
76
+
77
+ prepare_json
78
+ prepare(:apply)
79
+ end
80
+
81
+ # (see ChefBase#init_command)
82
+ def init_command
83
+ dirs = %w{
84
+ apply
85
+ }.sort.map { |dir| remote_path_join(config[:root_path], dir) }
86
+
87
+ vars = if powershell_shell?
88
+ init_command_vars_for_powershell(dirs)
89
+ else
90
+ init_command_vars_for_bourne(dirs)
91
+ end
92
+
93
+ prefix_command(shell_code_from_file(vars, "chef_base_init_command"))
94
+ end
95
+
96
+ # (see ChefSolo#run_command)
97
+ def run_command
98
+ level = config[:log_level]
99
+ lines = []
100
+ config[:run_list].map do |recipe|
101
+ cmd = sudo(config[:chef_apply_path]).dup
102
+ .tap { |str| str.insert(0, "& ") if powershell_shell? }
103
+ args = [
104
+ "apply/#{recipe}.rb",
105
+ "--log_level #{level}",
106
+ "--no-color",
107
+ ]
108
+ args << "--logfile #{config[:log_file]}" if config[:log_file]
109
+ args << "--chef-license #{config[:chef_license]}" if config[:chef_license]
110
+
111
+ lines << wrap_shell_code(
112
+ [cmd, *args].join(" ")
113
+ .tap { |str| str.insert(0, reload_ps1_path) if windows_os? }
114
+ )
115
+ end
116
+
117
+ prefix_command(lines.join("\n"))
118
+ end
119
+ end
120
+ end
121
+ end