test-kitchen 1.7.0 → 1.7.1.dev

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 (181) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +8 -8
  3. data/.gitattributes +3 -0
  4. data/.github/ISSUE_TEMPLATE.md +55 -55
  5. data/.gitignore +28 -28
  6. data/.kitchen.ci.yml +23 -23
  7. data/.kitchen.proxy.yml +27 -27
  8. data/.rubocop.yml +3 -3
  9. data/.travis.yml +70 -70
  10. data/.yardopts +3 -3
  11. data/Berksfile +3 -3
  12. data/CHANGELOG.md +1090 -1083
  13. data/CONTRIBUTING.md +14 -14
  14. data/Gemfile +19 -19
  15. data/Gemfile.proxy_tests +4 -4
  16. data/Guardfile +42 -42
  17. data/LICENSE +15 -15
  18. data/MAINTAINERS.md +23 -23
  19. data/README.md +135 -135
  20. data/Rakefile +61 -61
  21. data/appveyor.yml +44 -44
  22. data/features/kitchen_action_commands.feature +164 -164
  23. data/features/kitchen_command.feature +16 -16
  24. data/features/kitchen_console_command.feature +34 -34
  25. data/features/kitchen_defaults.feature +38 -38
  26. data/features/kitchen_diagnose_command.feature +96 -96
  27. data/features/kitchen_driver_create_command.feature +64 -64
  28. data/features/kitchen_driver_discover_command.feature +25 -25
  29. data/features/kitchen_help_command.feature +16 -16
  30. data/features/kitchen_init_command.feature +274 -274
  31. data/features/kitchen_list_command.feature +104 -104
  32. data/features/kitchen_login_command.feature +62 -62
  33. data/features/kitchen_sink_command.feature +30 -30
  34. data/features/kitchen_test_command.feature +88 -88
  35. data/features/step_definitions/gem_steps.rb +36 -36
  36. data/features/step_definitions/git_steps.rb +5 -5
  37. data/features/step_definitions/output_steps.rb +5 -5
  38. data/features/support/env.rb +75 -75
  39. data/lib/kitchen.rb +150 -150
  40. data/lib/kitchen/base64_stream.rb +55 -55
  41. data/lib/kitchen/cli.rb +419 -419
  42. data/lib/kitchen/collection.rb +55 -55
  43. data/lib/kitchen/color.rb +65 -65
  44. data/lib/kitchen/command.rb +185 -185
  45. data/lib/kitchen/command/action.rb +45 -45
  46. data/lib/kitchen/command/console.rb +58 -58
  47. data/lib/kitchen/command/diagnose.rb +92 -92
  48. data/lib/kitchen/command/driver_discover.rb +105 -105
  49. data/lib/kitchen/command/exec.rb +41 -41
  50. data/lib/kitchen/command/list.rb +119 -119
  51. data/lib/kitchen/command/login.rb +43 -43
  52. data/lib/kitchen/command/sink.rb +54 -54
  53. data/lib/kitchen/command/test.rb +51 -51
  54. data/lib/kitchen/config.rb +322 -322
  55. data/lib/kitchen/configurable.rb +529 -529
  56. data/lib/kitchen/data_munger.rb +959 -959
  57. data/lib/kitchen/diagnostic.rb +141 -141
  58. data/lib/kitchen/driver.rb +56 -56
  59. data/lib/kitchen/driver/base.rb +134 -134
  60. data/lib/kitchen/driver/dummy.rb +108 -108
  61. data/lib/kitchen/driver/proxy.rb +72 -72
  62. data/lib/kitchen/driver/ssh_base.rb +357 -357
  63. data/lib/kitchen/errors.rb +229 -229
  64. data/lib/kitchen/generator/driver_create.rb +177 -177
  65. data/lib/kitchen/generator/init.rb +296 -296
  66. data/lib/kitchen/instance.rb +662 -662
  67. data/lib/kitchen/lazy_hash.rb +142 -142
  68. data/lib/kitchen/loader/yaml.rb +349 -349
  69. data/lib/kitchen/logger.rb +423 -423
  70. data/lib/kitchen/logging.rb +56 -56
  71. data/lib/kitchen/login_command.rb +52 -52
  72. data/lib/kitchen/metadata_chopper.rb +52 -52
  73. data/lib/kitchen/platform.rb +67 -67
  74. data/lib/kitchen/provisioner.rb +54 -54
  75. data/lib/kitchen/provisioner/base.rb +236 -236
  76. data/lib/kitchen/provisioner/chef/berkshelf.rb +114 -114
  77. data/lib/kitchen/provisioner/chef/common_sandbox.rb +322 -322
  78. data/lib/kitchen/provisioner/chef/librarian.rb +112 -112
  79. data/lib/kitchen/provisioner/chef_apply.rb +124 -124
  80. data/lib/kitchen/provisioner/chef_base.rb +341 -341
  81. data/lib/kitchen/provisioner/chef_solo.rb +88 -88
  82. data/lib/kitchen/provisioner/chef_zero.rb +245 -245
  83. data/lib/kitchen/provisioner/dummy.rb +79 -79
  84. data/lib/kitchen/provisioner/shell.rb +138 -138
  85. data/lib/kitchen/rake_tasks.rb +63 -63
  86. data/lib/kitchen/shell_out.rb +93 -93
  87. data/lib/kitchen/ssh.rb +276 -276
  88. data/lib/kitchen/state_file.rb +120 -120
  89. data/lib/kitchen/suite.rb +51 -51
  90. data/lib/kitchen/thor_tasks.rb +66 -66
  91. data/lib/kitchen/transport.rb +54 -54
  92. data/lib/kitchen/transport/base.rb +176 -176
  93. data/lib/kitchen/transport/dummy.rb +79 -79
  94. data/lib/kitchen/transport/ssh.rb +364 -364
  95. data/lib/kitchen/transport/winrm.rb +486 -486
  96. data/lib/kitchen/util.rb +147 -147
  97. data/lib/kitchen/verifier.rb +55 -55
  98. data/lib/kitchen/verifier/base.rb +235 -235
  99. data/lib/kitchen/verifier/busser.rb +277 -277
  100. data/lib/kitchen/verifier/dummy.rb +79 -79
  101. data/lib/kitchen/verifier/shell.rb +101 -101
  102. data/lib/kitchen/version.rb +21 -21
  103. data/lib/vendor/hash_recursive_merge.rb +82 -82
  104. data/spec/kitchen/base64_stream_spec.rb +77 -77
  105. data/spec/kitchen/cli_spec.rb +56 -56
  106. data/spec/kitchen/collection_spec.rb +80 -80
  107. data/spec/kitchen/color_spec.rb +54 -54
  108. data/spec/kitchen/config_spec.rb +408 -408
  109. data/spec/kitchen/configurable_spec.rb +1095 -1095
  110. data/spec/kitchen/data_munger_spec.rb +2694 -2694
  111. data/spec/kitchen/diagnostic_spec.rb +129 -129
  112. data/spec/kitchen/driver/base_spec.rb +121 -121
  113. data/spec/kitchen/driver/dummy_spec.rb +199 -199
  114. data/spec/kitchen/driver/proxy_spec.rb +138 -138
  115. data/spec/kitchen/driver/ssh_base_spec.rb +1115 -1115
  116. data/spec/kitchen/driver_spec.rb +112 -112
  117. data/spec/kitchen/errors_spec.rb +309 -309
  118. data/spec/kitchen/instance_spec.rb +1419 -1419
  119. data/spec/kitchen/lazy_hash_spec.rb +117 -117
  120. data/spec/kitchen/loader/yaml_spec.rb +774 -774
  121. data/spec/kitchen/logger_spec.rb +429 -429
  122. data/spec/kitchen/logging_spec.rb +59 -59
  123. data/spec/kitchen/login_command_spec.rb +68 -68
  124. data/spec/kitchen/metadata_chopper_spec.rb +82 -82
  125. data/spec/kitchen/platform_spec.rb +89 -89
  126. data/spec/kitchen/provisioner/base_spec.rb +386 -386
  127. data/spec/kitchen/provisioner/chef_apply_spec.rb +136 -136
  128. data/spec/kitchen/provisioner/chef_base_spec.rb +1161 -1161
  129. data/spec/kitchen/provisioner/chef_solo_spec.rb +557 -557
  130. data/spec/kitchen/provisioner/chef_zero_spec.rb +1001 -1001
  131. data/spec/kitchen/provisioner/dummy_spec.rb +99 -99
  132. data/spec/kitchen/provisioner/shell_spec.rb +566 -566
  133. data/spec/kitchen/provisioner_spec.rb +107 -107
  134. data/spec/kitchen/shell_out_spec.rb +150 -150
  135. data/spec/kitchen/ssh_spec.rb +693 -693
  136. data/spec/kitchen/state_file_spec.rb +129 -129
  137. data/spec/kitchen/suite_spec.rb +62 -62
  138. data/spec/kitchen/transport/base_spec.rb +89 -89
  139. data/spec/kitchen/transport/ssh_spec.rb +1255 -1255
  140. data/spec/kitchen/transport/winrm_spec.rb +1143 -1143
  141. data/spec/kitchen/transport_spec.rb +112 -112
  142. data/spec/kitchen/util_spec.rb +165 -165
  143. data/spec/kitchen/verifier/base_spec.rb +362 -362
  144. data/spec/kitchen/verifier/busser_spec.rb +610 -610
  145. data/spec/kitchen/verifier/dummy_spec.rb +99 -99
  146. data/spec/kitchen/verifier/shell_spec.rb +160 -160
  147. data/spec/kitchen/verifier_spec.rb +120 -120
  148. data/spec/kitchen_spec.rb +114 -114
  149. data/spec/spec_helper.rb +85 -85
  150. data/spec/support/powershell_max_size_spec.rb +40 -40
  151. data/support/busser_install_command.ps1 +14 -14
  152. data/support/busser_install_command.sh +14 -14
  153. data/support/chef-client-zero.rb +77 -77
  154. data/support/chef_base_init_command.ps1 +18 -18
  155. data/support/chef_base_init_command.sh +2 -2
  156. data/support/chef_base_install_command.ps1 +85 -85
  157. data/support/chef_base_install_command.sh +229 -229
  158. data/support/chef_zero_prepare_command_legacy.ps1 +9 -9
  159. data/support/chef_zero_prepare_command_legacy.sh +10 -10
  160. data/support/download_helpers.sh +109 -109
  161. data/support/dummy-validation.pem +27 -27
  162. data/templates/driver/CHANGELOG.md.erb +3 -3
  163. data/templates/driver/Gemfile.erb +3 -3
  164. data/templates/driver/README.md.erb +64 -64
  165. data/templates/driver/Rakefile.erb +21 -21
  166. data/templates/driver/driver.rb.erb +23 -23
  167. data/templates/driver/gemspec.erb +29 -29
  168. data/templates/driver/gitignore.erb +17 -17
  169. data/templates/driver/license_apachev2.erb +15 -15
  170. data/templates/driver/license_lgplv3.erb +16 -16
  171. data/templates/driver/license_mit.erb +22 -22
  172. data/templates/driver/license_reserved.erb +5 -5
  173. data/templates/driver/tailor.erb +4 -4
  174. data/templates/driver/travis.yml.erb +11 -11
  175. data/templates/driver/version.rb.erb +12 -12
  176. data/templates/init/chefignore.erb +1 -1
  177. data/templates/init/kitchen.yml.erb +18 -18
  178. data/test-kitchen.gemspec +62 -62
  179. data/test/integration/default/default_spec.rb +3 -3
  180. data/testing_windows.md +37 -37
  181. metadata +5 -4
@@ -1,341 +1,341 @@
1
- # -*- encoding: utf-8 -*-
2
- #
3
- # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
- #
5
- # Copyright (C) 2013, Fletcher Nichol
6
- #
7
- # Licensed under the Apache License, Version 2.0 (the "License");
8
- # you may not use this file except in compliance with the License.
9
- # You may obtain a copy of the License at
10
- #
11
- # http://www.apache.org/licenses/LICENSE-2.0
12
- #
13
- # Unless required by applicable law or agreed to in writing, software
14
- # distributed under the License is distributed on an "AS IS" BASIS,
15
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
- # See the License for the specific language governing permissions and
17
- # limitations under the License.
18
-
19
- require "fileutils"
20
- require "pathname"
21
- require "json"
22
- require "cgi"
23
-
24
- require "kitchen/provisioner/chef/berkshelf"
25
- require "kitchen/provisioner/chef/common_sandbox"
26
- require "kitchen/provisioner/chef/librarian"
27
- require "kitchen/util"
28
- require "mixlib/install"
29
- require "mixlib/install/script_generator"
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
-
40
- module Provisioner
41
-
42
- # Common implementation details for Chef-related provisioners.
43
- #
44
- # @author Fletcher Nichol <fnichol@nichol.ca>
45
- class ChefBase < Base
46
-
47
- default_config :require_chef_omnibus, true
48
- default_config :chef_omnibus_url, "https://www.chef.io/chef/install.sh"
49
- default_config :chef_omnibus_install_options, nil
50
- default_config :run_list, []
51
- default_config :attributes, {}
52
- default_config :config_path, nil
53
- default_config :log_file, nil
54
- default_config :log_level, "auto"
55
- default_config :profile_ruby, false
56
- default_config :cookbook_files_glob, %w[
57
- README.* metadata.{json,rb}
58
- attributes/**/* definitions/**/* files/**/* libraries/**/*
59
- providers/**/* recipes/**/* resources/**/* templates/**/*
60
- ].join(",")
61
-
62
- default_config :data_path do |provisioner|
63
- provisioner.calculate_path("data")
64
- end
65
- expand_path_for :data_path
66
-
67
- default_config :data_bags_path do |provisioner|
68
- provisioner.calculate_path("data_bags")
69
- end
70
- expand_path_for :data_bags_path
71
-
72
- default_config :environments_path do |provisioner|
73
- provisioner.calculate_path("environments")
74
- end
75
- expand_path_for :environments_path
76
-
77
- default_config :nodes_path do |provisioner|
78
- provisioner.calculate_path("nodes")
79
- end
80
- expand_path_for :nodes_path
81
-
82
- default_config :roles_path do |provisioner|
83
- provisioner.calculate_path("roles")
84
- end
85
- expand_path_for :roles_path
86
-
87
- default_config :clients_path do |provisioner|
88
- provisioner.calculate_path("clients")
89
- end
90
- expand_path_for :clients_path
91
-
92
- default_config :encrypted_data_bag_secret_key_path do |provisioner|
93
- provisioner.calculate_path("encrypted_data_bag_secret_key", :type => :file)
94
- end
95
- expand_path_for :encrypted_data_bag_secret_key_path
96
-
97
- # Reads the local Chef::Config object (if present). We do this because
98
- # we want to start bring Chef config and ChefDK tool config closer
99
- # together. For example, we want to configure proxy settings in 1
100
- # location instead of 3 configuration files.
101
- #
102
- # @param config [Hash] initial provided configuration
103
- def initialize(config = {})
104
- super(config)
105
-
106
- if defined?(ChefConfig::WorkstationConfigLoader)
107
- ChefConfig::WorkstationConfigLoader.new(config[:config_path]).load
108
- end
109
- # This exports any proxy config present in the Chef config to
110
- # appropriate environment variables, which Test Kitchen respects
111
- ChefConfig::Config.export_proxies if defined?(ChefConfig::Config.export_proxies)
112
- end
113
-
114
- # (see Base#create_sandbox)
115
- def create_sandbox
116
- super
117
- Chef::CommonSandbox.new(config, sandbox_path, instance).populate
118
- end
119
-
120
- # (see Base#init_command)
121
- def init_command
122
- dirs = %w[
123
- cookbooks data data_bags environments roles clients
124
- encrypted_data_bag_secret
125
- ].sort.map { |dir| remote_path_join(config[:root_path], dir) }
126
-
127
- vars = if powershell_shell?
128
- init_command_vars_for_powershell(dirs)
129
- else
130
- init_command_vars_for_bourne(dirs)
131
- end
132
-
133
- prefix_command(shell_code_from_file(vars, "chef_base_init_command"))
134
- end
135
-
136
- # (see Base#install_command)
137
- def install_command
138
- return unless config[:require_chef_omnibus] || config[:product_name]
139
- prefix_command(sudo(install_script_contents))
140
- end
141
-
142
- private
143
-
144
- # @return [Hash] an option hash for the install commands
145
- # @api private
146
- def install_options
147
- project = /\s*-P (\w+)\s*/.match(config[:chef_omnibus_install_options])
148
- {
149
- :omnibus_url => config[:chef_omnibus_url],
150
- :project => project.nil? ? nil : project[1],
151
- :install_flags => config[:chef_omnibus_install_options],
152
- :sudo_command => sudo_command
153
- }.tap do |opts|
154
- opts[:root] = config[:chef_omnibus_root] if config.key? :chef_omnibus_root
155
- [:install_msi_url, :http_proxy, :https_proxy].each do |key|
156
- opts[key] = config[key] if config.key? key
157
- end
158
- end
159
- end
160
-
161
- # @return [String] an absolute path to a Berksfile, relative to the
162
- # kitchen root
163
- # @api private
164
- def berksfile
165
- File.join(config[:kitchen_root], "Berksfile")
166
- end
167
-
168
- # @return [String] an absolute path to a Cheffile, relative to the
169
- # kitchen root
170
- # @api private
171
- def cheffile
172
- File.join(config[:kitchen_root], "Cheffile")
173
- end
174
-
175
- # Generates a Hash with default values for a solo.rb or client.rb Chef
176
- # configuration file.
177
- #
178
- # @return [Hash] a configuration hash
179
- # @api private
180
- def default_config_rb # rubocop:disable Metrics/MethodLength
181
- root = config[:root_path].gsub("$env:TEMP", "\#{ENV['TEMP']\}")
182
-
183
- {
184
- :node_name => instance.name,
185
- :checksum_path => remote_path_join(root, "checksums"),
186
- :file_cache_path => remote_path_join(root, "cache"),
187
- :file_backup_path => remote_path_join(root, "backup"),
188
- :cookbook_path => [
189
- remote_path_join(root, "cookbooks"),
190
- remote_path_join(root, "site-cookbooks")
191
- ],
192
- :data_bag_path => remote_path_join(root, "data_bags"),
193
- :environment_path => remote_path_join(root, "environments"),
194
- :node_path => remote_path_join(root, "nodes"),
195
- :role_path => remote_path_join(root, "roles"),
196
- :client_path => remote_path_join(root, "clients"),
197
- :user_path => remote_path_join(root, "users"),
198
- :validation_key => remote_path_join(root, "validation.pem"),
199
- :client_key => remote_path_join(root, "client.pem"),
200
- :chef_server_url => "http://127.0.0.1:8889",
201
- :encrypted_data_bag_secret => remote_path_join(
202
- root, "encrypted_data_bag_secret"
203
- )
204
- }
205
- end
206
-
207
- # Generates a rendered client.rb/solo.rb/knife.rb formatted file as a
208
- # String.
209
- #
210
- # @param data [Hash] a key/value pair hash of configuration
211
- # @return [String] a rendered Chef config file as a String
212
- # @api private
213
- def format_config_file(data)
214
- data.each.map { |attr, value|
215
- [attr, format_value(value)].join(" ")
216
- }.join("\n")
217
- end
218
-
219
- # Converts a Ruby object to a String interpretation suitable for writing
220
- # out to a client.rb/solo.rb/knife.rb file.
221
- #
222
- # @param obj [Object] an object
223
- # @return [String] a string representation
224
- # @api private
225
- def format_value(obj)
226
- if obj.is_a?(String) && obj =~ /^:/
227
- obj
228
- elsif obj.is_a?(String)
229
- %{"#{obj.gsub(/\\/, "\\\\\\\\")}"}
230
- elsif obj.is_a?(Array)
231
- %{[#{obj.map { |i| format_value(i) }.join(", ")}]}
232
- else
233
- obj.inspect
234
- end
235
- end
236
-
237
- # Generates the init command variables for Bourne shell-based platforms.
238
- #
239
- # @param dirs [Array<String>] directories
240
- # @return [String] shell variable lines
241
- # @api private
242
- def init_command_vars_for_bourne(dirs)
243
- [
244
- shell_var("sudo_rm", sudo("rm")),
245
- shell_var("dirs", dirs.join(" ")),
246
- shell_var("root_path", config[:root_path])
247
- ].join("\n")
248
- end
249
-
250
- # Generates the init command variables for PowerShell-based platforms.
251
- #
252
- # @param dirs [Array<String>] directories
253
- # @return [String] shell variable lines
254
- # @api private
255
- def init_command_vars_for_powershell(dirs)
256
- [
257
- %{$dirs = @(#{dirs.map { |d| %{"#{d}"} }.join(", ")})},
258
- shell_var("root_path", config[:root_path])
259
- ].join("\n")
260
- end
261
-
262
- # Load cookbook dependency resolver code, if required.
263
- #
264
- # (see Base#load_needed_dependencies!)
265
- def load_needed_dependencies!
266
- super
267
- if File.exist?(berksfile)
268
- debug("Berksfile found at #{berksfile}, loading Berkshelf")
269
- Chef::Berkshelf.load!(logger)
270
- elsif File.exist?(cheffile)
271
- debug("Cheffile found at #{cheffile}, loading Librarian-Chef")
272
- Chef::Librarian.load!(logger)
273
- end
274
- end
275
-
276
- # @return [String] a powershell command to reload the `PATH` environment
277
- # variable, only to be used to support old Omnibus Chef packages that
278
- # require `PATH` to find the `ruby.exe` binary
279
- # @api private
280
- def reload_ps1_path
281
- [
282
- %{$env:PATH},
283
- %{[System.Environment]::GetEnvironmentVariable("PATH","Machine")\n\n}
284
- ].join(" = ")
285
- end
286
-
287
- # @return [String] contents of the install script
288
- # @api private
289
- def install_script_contents
290
- # by default require_chef_omnibus is set to true. Check config[:product_name] first
291
- # so that we can use it if configured.
292
- if config[:product_name]
293
- script_for_product
294
- elsif config[:require_chef_omnibus]
295
- script_for_omnibus_version
296
- end
297
- end
298
-
299
- # @return [String] contents of product based install script
300
- # @api private
301
- def script_for_product
302
- installer = Mixlib::Install.new({
303
- :product_name => config[:product_name],
304
- :product_version => config[:product_version],
305
- :channel => (config[:channel] || :stable).to_sym
306
- }.tap do |opts|
307
- opts[:shell_type] = :ps1 if powershell_shell?
308
- [:platform, :platform_version, :architecture].each do |key|
309
- opts[key] = config[key] if config[key]
310
- end
311
- end)
312
- config[:chef_omnibus_root] = installer.root
313
- if powershell_shell?
314
- installer.install_command
315
- else
316
- install_from_file(installer.install_command)
317
- end
318
- end
319
-
320
- def install_from_file(command)
321
- install_file = "/tmp/chef-installer.sh"
322
- script = ["cat > #{install_file} <<\"EOL\""]
323
- script << "#!/bin/bash"
324
- script << command
325
- script << "EOL"
326
- script << "chmod +x #{install_file}"
327
- script << sudo(install_file)
328
- script.join("\n")
329
- end
330
-
331
- # @return [String] contents of version based install script
332
- # @api private
333
- def script_for_omnibus_version
334
- installer = Mixlib::Install::ScriptGenerator.new(
335
- config[:require_chef_omnibus], powershell_shell?, install_options)
336
- config[:chef_omnibus_root] = installer.root
337
- installer.install_command
338
- end
339
- end
340
- end
341
- end
1
+ # -*- encoding: utf-8 -*-
2
+ #
3
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
4
+ #
5
+ # Copyright (C) 2013, Fletcher Nichol
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+
19
+ require "fileutils"
20
+ require "pathname"
21
+ require "json"
22
+ require "cgi"
23
+
24
+ require "kitchen/provisioner/chef/berkshelf"
25
+ require "kitchen/provisioner/chef/common_sandbox"
26
+ require "kitchen/provisioner/chef/librarian"
27
+ require "kitchen/util"
28
+ require "mixlib/install"
29
+ require "mixlib/install/script_generator"
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
+
40
+ module Provisioner
41
+
42
+ # Common implementation details for Chef-related provisioners.
43
+ #
44
+ # @author Fletcher Nichol <fnichol@nichol.ca>
45
+ class ChefBase < Base
46
+
47
+ default_config :require_chef_omnibus, true
48
+ default_config :chef_omnibus_url, "https://www.chef.io/chef/install.sh"
49
+ default_config :chef_omnibus_install_options, nil
50
+ default_config :run_list, []
51
+ default_config :attributes, {}
52
+ default_config :config_path, nil
53
+ default_config :log_file, nil
54
+ default_config :log_level, "auto"
55
+ default_config :profile_ruby, false
56
+ default_config :cookbook_files_glob, %w[
57
+ README.* metadata.{json,rb}
58
+ attributes/**/* definitions/**/* files/**/* libraries/**/*
59
+ providers/**/* recipes/**/* resources/**/* templates/**/*
60
+ ].join(",")
61
+
62
+ default_config :data_path do |provisioner|
63
+ provisioner.calculate_path("data")
64
+ end
65
+ expand_path_for :data_path
66
+
67
+ default_config :data_bags_path do |provisioner|
68
+ provisioner.calculate_path("data_bags")
69
+ end
70
+ expand_path_for :data_bags_path
71
+
72
+ default_config :environments_path do |provisioner|
73
+ provisioner.calculate_path("environments")
74
+ end
75
+ expand_path_for :environments_path
76
+
77
+ default_config :nodes_path do |provisioner|
78
+ provisioner.calculate_path("nodes")
79
+ end
80
+ expand_path_for :nodes_path
81
+
82
+ default_config :roles_path do |provisioner|
83
+ provisioner.calculate_path("roles")
84
+ end
85
+ expand_path_for :roles_path
86
+
87
+ default_config :clients_path do |provisioner|
88
+ provisioner.calculate_path("clients")
89
+ end
90
+ expand_path_for :clients_path
91
+
92
+ default_config :encrypted_data_bag_secret_key_path do |provisioner|
93
+ provisioner.calculate_path("encrypted_data_bag_secret_key", :type => :file)
94
+ end
95
+ expand_path_for :encrypted_data_bag_secret_key_path
96
+
97
+ # Reads the local Chef::Config object (if present). We do this because
98
+ # we want to start bring Chef config and ChefDK tool config closer
99
+ # together. For example, we want to configure proxy settings in 1
100
+ # location instead of 3 configuration files.
101
+ #
102
+ # @param config [Hash] initial provided configuration
103
+ def initialize(config = {})
104
+ super(config)
105
+
106
+ if defined?(ChefConfig::WorkstationConfigLoader)
107
+ ChefConfig::WorkstationConfigLoader.new(config[:config_path]).load
108
+ end
109
+ # This exports any proxy config present in the Chef config to
110
+ # appropriate environment variables, which Test Kitchen respects
111
+ ChefConfig::Config.export_proxies if defined?(ChefConfig::Config.export_proxies)
112
+ end
113
+
114
+ # (see Base#create_sandbox)
115
+ def create_sandbox
116
+ super
117
+ Chef::CommonSandbox.new(config, sandbox_path, instance).populate
118
+ end
119
+
120
+ # (see Base#init_command)
121
+ def init_command
122
+ dirs = %w[
123
+ cookbooks data data_bags environments roles clients
124
+ encrypted_data_bag_secret
125
+ ].sort.map { |dir| remote_path_join(config[:root_path], dir) }
126
+
127
+ vars = if powershell_shell?
128
+ init_command_vars_for_powershell(dirs)
129
+ else
130
+ init_command_vars_for_bourne(dirs)
131
+ end
132
+
133
+ prefix_command(shell_code_from_file(vars, "chef_base_init_command"))
134
+ end
135
+
136
+ # (see Base#install_command)
137
+ def install_command
138
+ return unless config[:require_chef_omnibus] || config[:product_name]
139
+ prefix_command(sudo(install_script_contents))
140
+ end
141
+
142
+ private
143
+
144
+ # @return [Hash] an option hash for the install commands
145
+ # @api private
146
+ def install_options
147
+ project = /\s*-P (\w+)\s*/.match(config[:chef_omnibus_install_options])
148
+ {
149
+ :omnibus_url => config[:chef_omnibus_url],
150
+ :project => project.nil? ? nil : project[1],
151
+ :install_flags => config[:chef_omnibus_install_options],
152
+ :sudo_command => sudo_command
153
+ }.tap do |opts|
154
+ opts[:root] = config[:chef_omnibus_root] if config.key? :chef_omnibus_root
155
+ [:install_msi_url, :http_proxy, :https_proxy].each do |key|
156
+ opts[key] = config[key] if config.key? key
157
+ end
158
+ end
159
+ end
160
+
161
+ # @return [String] an absolute path to a Berksfile, relative to the
162
+ # kitchen root
163
+ # @api private
164
+ def berksfile
165
+ File.join(config[:kitchen_root], "Berksfile")
166
+ end
167
+
168
+ # @return [String] an absolute path to a Cheffile, relative to the
169
+ # kitchen root
170
+ # @api private
171
+ def cheffile
172
+ File.join(config[:kitchen_root], "Cheffile")
173
+ end
174
+
175
+ # Generates a Hash with default values for a solo.rb or client.rb Chef
176
+ # configuration file.
177
+ #
178
+ # @return [Hash] a configuration hash
179
+ # @api private
180
+ def default_config_rb # rubocop:disable Metrics/MethodLength
181
+ root = config[:root_path].gsub("$env:TEMP", "\#{ENV['TEMP']\}")
182
+
183
+ {
184
+ :node_name => instance.name,
185
+ :checksum_path => remote_path_join(root, "checksums"),
186
+ :file_cache_path => remote_path_join(root, "cache"),
187
+ :file_backup_path => remote_path_join(root, "backup"),
188
+ :cookbook_path => [
189
+ remote_path_join(root, "cookbooks"),
190
+ remote_path_join(root, "site-cookbooks")
191
+ ],
192
+ :data_bag_path => remote_path_join(root, "data_bags"),
193
+ :environment_path => remote_path_join(root, "environments"),
194
+ :node_path => remote_path_join(root, "nodes"),
195
+ :role_path => remote_path_join(root, "roles"),
196
+ :client_path => remote_path_join(root, "clients"),
197
+ :user_path => remote_path_join(root, "users"),
198
+ :validation_key => remote_path_join(root, "validation.pem"),
199
+ :client_key => remote_path_join(root, "client.pem"),
200
+ :chef_server_url => "http://127.0.0.1:8889",
201
+ :encrypted_data_bag_secret => remote_path_join(
202
+ root, "encrypted_data_bag_secret"
203
+ )
204
+ }
205
+ end
206
+
207
+ # Generates a rendered client.rb/solo.rb/knife.rb formatted file as a
208
+ # String.
209
+ #
210
+ # @param data [Hash] a key/value pair hash of configuration
211
+ # @return [String] a rendered Chef config file as a String
212
+ # @api private
213
+ def format_config_file(data)
214
+ data.each.map { |attr, value|
215
+ [attr, format_value(value)].join(" ")
216
+ }.join("\n")
217
+ end
218
+
219
+ # Converts a Ruby object to a String interpretation suitable for writing
220
+ # out to a client.rb/solo.rb/knife.rb file.
221
+ #
222
+ # @param obj [Object] an object
223
+ # @return [String] a string representation
224
+ # @api private
225
+ def format_value(obj)
226
+ if obj.is_a?(String) && obj =~ /^:/
227
+ obj
228
+ elsif obj.is_a?(String)
229
+ %{"#{obj.gsub(/\\/, "\\\\\\\\")}"}
230
+ elsif obj.is_a?(Array)
231
+ %{[#{obj.map { |i| format_value(i) }.join(", ")}]}
232
+ else
233
+ obj.inspect
234
+ end
235
+ end
236
+
237
+ # Generates the init command variables for Bourne shell-based platforms.
238
+ #
239
+ # @param dirs [Array<String>] directories
240
+ # @return [String] shell variable lines
241
+ # @api private
242
+ def init_command_vars_for_bourne(dirs)
243
+ [
244
+ shell_var("sudo_rm", sudo("rm")),
245
+ shell_var("dirs", dirs.join(" ")),
246
+ shell_var("root_path", config[:root_path])
247
+ ].join("\n")
248
+ end
249
+
250
+ # Generates the init command variables for PowerShell-based platforms.
251
+ #
252
+ # @param dirs [Array<String>] directories
253
+ # @return [String] shell variable lines
254
+ # @api private
255
+ def init_command_vars_for_powershell(dirs)
256
+ [
257
+ %{$dirs = @(#{dirs.map { |d| %{"#{d}"} }.join(", ")})},
258
+ shell_var("root_path", config[:root_path])
259
+ ].join("\n")
260
+ end
261
+
262
+ # Load cookbook dependency resolver code, if required.
263
+ #
264
+ # (see Base#load_needed_dependencies!)
265
+ def load_needed_dependencies!
266
+ super
267
+ if File.exist?(berksfile)
268
+ debug("Berksfile found at #{berksfile}, loading Berkshelf")
269
+ Chef::Berkshelf.load!(logger)
270
+ elsif File.exist?(cheffile)
271
+ debug("Cheffile found at #{cheffile}, loading Librarian-Chef")
272
+ Chef::Librarian.load!(logger)
273
+ end
274
+ end
275
+
276
+ # @return [String] a powershell command to reload the `PATH` environment
277
+ # variable, only to be used to support old Omnibus Chef packages that
278
+ # require `PATH` to find the `ruby.exe` binary
279
+ # @api private
280
+ def reload_ps1_path
281
+ [
282
+ %{$env:PATH},
283
+ %{[System.Environment]::GetEnvironmentVariable("PATH","Machine")\n\n}
284
+ ].join(" = ")
285
+ end
286
+
287
+ # @return [String] contents of the install script
288
+ # @api private
289
+ def install_script_contents
290
+ # by default require_chef_omnibus is set to true. Check config[:product_name] first
291
+ # so that we can use it if configured.
292
+ if config[:product_name]
293
+ script_for_product
294
+ elsif config[:require_chef_omnibus]
295
+ script_for_omnibus_version
296
+ end
297
+ end
298
+
299
+ # @return [String] contents of product based install script
300
+ # @api private
301
+ def script_for_product
302
+ installer = Mixlib::Install.new({
303
+ :product_name => config[:product_name],
304
+ :product_version => config[:product_version],
305
+ :channel => (config[:channel] || :stable).to_sym
306
+ }.tap do |opts|
307
+ opts[:shell_type] = :ps1 if powershell_shell?
308
+ [:platform, :platform_version, :architecture].each do |key|
309
+ opts[key] = config[key] if config[key]
310
+ end
311
+ end)
312
+ config[:chef_omnibus_root] = installer.root
313
+ if powershell_shell?
314
+ installer.install_command
315
+ else
316
+ install_from_file(installer.install_command)
317
+ end
318
+ end
319
+
320
+ def install_from_file(command)
321
+ install_file = "/tmp/chef-installer.sh"
322
+ script = ["cat > #{install_file} <<\"EOL\""]
323
+ script << "#!/bin/bash"
324
+ script << command
325
+ script << "EOL"
326
+ script << "chmod +x #{install_file}"
327
+ script << sudo(install_file)
328
+ script.join("\n")
329
+ end
330
+
331
+ # @return [String] contents of version based install script
332
+ # @api private
333
+ def script_for_omnibus_version
334
+ installer = Mixlib::Install::ScriptGenerator.new(
335
+ config[:require_chef_omnibus], powershell_shell?, install_options)
336
+ config[:chef_omnibus_root] = installer.root
337
+ installer.install_command
338
+ end
339
+ end
340
+ end
341
+ end