test-kitchen-rsync 3.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +21 -0
  3. data/LICENSE +15 -0
  4. data/Rakefile +53 -0
  5. data/bin/zl-kitchen +11 -0
  6. data/lib/kitchen/base64_stream.rb +48 -0
  7. data/lib/kitchen/chef_utils_wiring.rb +40 -0
  8. data/lib/kitchen/cli.rb +413 -0
  9. data/lib/kitchen/collection.rb +52 -0
  10. data/lib/kitchen/color.rb +63 -0
  11. data/lib/kitchen/command/action.rb +41 -0
  12. data/lib/kitchen/command/console.rb +54 -0
  13. data/lib/kitchen/command/diagnose.rb +84 -0
  14. data/lib/kitchen/command/doctor.rb +39 -0
  15. data/lib/kitchen/command/exec.rb +37 -0
  16. data/lib/kitchen/command/list.rb +148 -0
  17. data/lib/kitchen/command/login.rb +39 -0
  18. data/lib/kitchen/command/package.rb +32 -0
  19. data/lib/kitchen/command/sink.rb +50 -0
  20. data/lib/kitchen/command/test.rb +47 -0
  21. data/lib/kitchen/command.rb +207 -0
  22. data/lib/kitchen/config.rb +344 -0
  23. data/lib/kitchen/configurable.rb +616 -0
  24. data/lib/kitchen/data_munger.rb +1024 -0
  25. data/lib/kitchen/diagnostic.rb +138 -0
  26. data/lib/kitchen/driver/base.rb +133 -0
  27. data/lib/kitchen/driver/dummy.rb +105 -0
  28. data/lib/kitchen/driver/exec.rb +70 -0
  29. data/lib/kitchen/driver/proxy.rb +70 -0
  30. data/lib/kitchen/driver/ssh_base.rb +351 -0
  31. data/lib/kitchen/driver.rb +40 -0
  32. data/lib/kitchen/errors.rb +243 -0
  33. data/lib/kitchen/generator/init.rb +254 -0
  34. data/lib/kitchen/instance.rb +726 -0
  35. data/lib/kitchen/lazy_hash.rb +148 -0
  36. data/lib/kitchen/lifecycle_hook/base.rb +78 -0
  37. data/lib/kitchen/lifecycle_hook/local.rb +53 -0
  38. data/lib/kitchen/lifecycle_hook/remote.rb +39 -0
  39. data/lib/kitchen/lifecycle_hooks.rb +92 -0
  40. data/lib/kitchen/loader/yaml.rb +377 -0
  41. data/lib/kitchen/logger.rb +422 -0
  42. data/lib/kitchen/logging.rb +52 -0
  43. data/lib/kitchen/login_command.rb +49 -0
  44. data/lib/kitchen/metadata_chopper.rb +49 -0
  45. data/lib/kitchen/platform.rb +64 -0
  46. data/lib/kitchen/plugin.rb +76 -0
  47. data/lib/kitchen/plugin_base.rb +60 -0
  48. data/lib/kitchen/provisioner/base.rb +269 -0
  49. data/lib/kitchen/provisioner/chef/berkshelf.rb +116 -0
  50. data/lib/kitchen/provisioner/chef/common_sandbox.rb +350 -0
  51. data/lib/kitchen/provisioner/chef/policyfile.rb +163 -0
  52. data/lib/kitchen/provisioner/chef_apply.rb +121 -0
  53. data/lib/kitchen/provisioner/chef_base.rb +705 -0
  54. data/lib/kitchen/provisioner/chef_infra.rb +167 -0
  55. data/lib/kitchen/provisioner/chef_solo.rb +82 -0
  56. data/lib/kitchen/provisioner/chef_zero.rb +12 -0
  57. data/lib/kitchen/provisioner/dummy.rb +75 -0
  58. data/lib/kitchen/provisioner/shell.rb +157 -0
  59. data/lib/kitchen/provisioner.rb +42 -0
  60. data/lib/kitchen/rake_tasks.rb +80 -0
  61. data/lib/kitchen/shell_out.rb +90 -0
  62. data/lib/kitchen/ssh.rb +289 -0
  63. data/lib/kitchen/state_file.rb +112 -0
  64. data/lib/kitchen/suite.rb +48 -0
  65. data/lib/kitchen/thor_tasks.rb +63 -0
  66. data/lib/kitchen/transport/base.rb +236 -0
  67. data/lib/kitchen/transport/dummy.rb +78 -0
  68. data/lib/kitchen/transport/exec.rb +145 -0
  69. data/lib/kitchen/transport/ssh.rb +579 -0
  70. data/lib/kitchen/transport/winrm.rb +546 -0
  71. data/lib/kitchen/transport.rb +40 -0
  72. data/lib/kitchen/util.rb +229 -0
  73. data/lib/kitchen/verifier/base.rb +243 -0
  74. data/lib/kitchen/verifier/busser.rb +275 -0
  75. data/lib/kitchen/verifier/dummy.rb +75 -0
  76. data/lib/kitchen/verifier/shell.rb +99 -0
  77. data/lib/kitchen/verifier.rb +39 -0
  78. data/lib/kitchen/version.rb +20 -0
  79. data/lib/kitchen/which.rb +26 -0
  80. data/lib/kitchen.rb +152 -0
  81. data/lib/vendor/hash_recursive_merge.rb +79 -0
  82. data/support/busser_install_command.ps1 +14 -0
  83. data/support/busser_install_command.sh +21 -0
  84. data/support/chef-client-fail-if-update-handler.rb +15 -0
  85. data/support/chef_base_init_command.ps1 +18 -0
  86. data/support/chef_base_init_command.sh +1 -0
  87. data/support/chef_base_install_command.ps1 +85 -0
  88. data/support/chef_base_install_command.sh +229 -0
  89. data/support/download_helpers.sh +109 -0
  90. data/support/dummy-validation.pem +27 -0
  91. data/templates/driver/CHANGELOG.md.erb +3 -0
  92. data/templates/driver/Gemfile.erb +3 -0
  93. data/templates/driver/README.md.erb +64 -0
  94. data/templates/driver/Rakefile.erb +21 -0
  95. data/templates/driver/driver.rb.erb +23 -0
  96. data/templates/driver/gemspec.erb +29 -0
  97. data/templates/driver/gitignore.erb +17 -0
  98. data/templates/driver/license_apachev2.erb +15 -0
  99. data/templates/driver/license_lgplv3.erb +16 -0
  100. data/templates/driver/license_mit.erb +22 -0
  101. data/templates/driver/license_reserved.erb +5 -0
  102. data/templates/driver/tailor.erb +4 -0
  103. data/templates/driver/travis.yml.erb +11 -0
  104. data/templates/driver/version.rb.erb +12 -0
  105. data/templates/init/chefignore.erb +2 -0
  106. data/templates/init/kitchen.yml.erb +18 -0
  107. data/test-kitchen.gemspec +52 -0
  108. metadata +528 -0
@@ -0,0 +1,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