test-kitchen 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/.cane +1 -1
  3. data/.rubocop.yml +3 -0
  4. data/.travis.yml +20 -9
  5. data/CHANGELOG.md +219 -108
  6. data/Gemfile +10 -6
  7. data/Guardfile +38 -9
  8. data/README.md +11 -1
  9. data/Rakefile +21 -37
  10. data/bin/kitchen +4 -4
  11. data/features/kitchen_action_commands.feature +161 -0
  12. data/features/kitchen_console_command.feature +34 -0
  13. data/features/kitchen_diagnose_command.feature +64 -0
  14. data/features/kitchen_init_command.feature +29 -17
  15. data/features/kitchen_list_command.feature +2 -2
  16. data/features/kitchen_login_command.feature +56 -0
  17. data/features/{sink_command.feature → kitchen_sink_command.feature} +0 -0
  18. data/features/kitchen_test_command.feature +88 -0
  19. data/features/step_definitions/gem_steps.rb +8 -6
  20. data/features/step_definitions/git_steps.rb +4 -2
  21. data/features/step_definitions/output_steps.rb +5 -0
  22. data/features/support/env.rb +12 -9
  23. data/lib/kitchen.rb +60 -38
  24. data/lib/kitchen/base64_stream.rb +55 -0
  25. data/lib/kitchen/busser.rb +124 -58
  26. data/lib/kitchen/cli.rb +121 -38
  27. data/lib/kitchen/collection.rb +3 -3
  28. data/lib/kitchen/color.rb +4 -4
  29. data/lib/kitchen/command.rb +78 -11
  30. data/lib/kitchen/command/action.rb +3 -2
  31. data/lib/kitchen/command/console.rb +12 -5
  32. data/lib/kitchen/command/diagnose.rb +17 -3
  33. data/lib/kitchen/command/driver_discover.rb +26 -7
  34. data/lib/kitchen/command/exec.rb +41 -0
  35. data/lib/kitchen/command/list.rb +44 -14
  36. data/lib/kitchen/command/login.rb +2 -1
  37. data/lib/kitchen/command/sink.rb +2 -1
  38. data/lib/kitchen/command/test.rb +5 -4
  39. data/lib/kitchen/config.rb +146 -14
  40. data/lib/kitchen/configurable.rb +314 -0
  41. data/lib/kitchen/data_munger.rb +522 -18
  42. data/lib/kitchen/diagnostic.rb +43 -4
  43. data/lib/kitchen/driver.rb +4 -4
  44. data/lib/kitchen/driver/base.rb +80 -115
  45. data/lib/kitchen/driver/dummy.rb +34 -6
  46. data/lib/kitchen/driver/proxy.rb +14 -3
  47. data/lib/kitchen/driver/ssh_base.rb +61 -7
  48. data/lib/kitchen/errors.rb +109 -9
  49. data/lib/kitchen/generator/driver_create.rb +39 -5
  50. data/lib/kitchen/generator/init.rb +130 -45
  51. data/lib/kitchen/instance.rb +162 -28
  52. data/lib/kitchen/lazy_hash.rb +79 -7
  53. data/lib/kitchen/loader/yaml.rb +159 -27
  54. data/lib/kitchen/logger.rb +267 -21
  55. data/lib/kitchen/logging.rb +30 -3
  56. data/lib/kitchen/login_command.rb +11 -2
  57. data/lib/kitchen/metadata_chopper.rb +2 -2
  58. data/lib/kitchen/provisioner.rb +4 -4
  59. data/lib/kitchen/provisioner/base.rb +107 -103
  60. data/lib/kitchen/provisioner/chef/berkshelf.rb +36 -8
  61. data/lib/kitchen/provisioner/chef/librarian.rb +40 -11
  62. data/lib/kitchen/provisioner/chef_base.rb +206 -167
  63. data/lib/kitchen/provisioner/chef_solo.rb +25 -7
  64. data/lib/kitchen/provisioner/chef_zero.rb +105 -29
  65. data/lib/kitchen/provisioner/dummy.rb +1 -1
  66. data/lib/kitchen/provisioner/shell.rb +21 -6
  67. data/lib/kitchen/rake_tasks.rb +8 -3
  68. data/lib/kitchen/shell_out.rb +15 -18
  69. data/lib/kitchen/ssh.rb +122 -27
  70. data/lib/kitchen/state_file.rb +24 -7
  71. data/lib/kitchen/thor_tasks.rb +9 -4
  72. data/lib/kitchen/util.rb +43 -118
  73. data/lib/kitchen/version.rb +1 -1
  74. data/lib/vendor/hash_recursive_merge.rb +10 -2
  75. data/spec/kitchen/base64_stream_spec.rb +77 -0
  76. data/spec/kitchen/busser_spec.rb +490 -0
  77. data/spec/kitchen/collection_spec.rb +10 -10
  78. data/spec/kitchen/color_spec.rb +2 -2
  79. data/spec/kitchen/config_spec.rb +234 -62
  80. data/spec/kitchen/configurable_spec.rb +490 -0
  81. data/spec/kitchen/data_munger_spec.rb +1070 -862
  82. data/spec/kitchen/diagnostic_spec.rb +79 -0
  83. data/spec/kitchen/driver/base_spec.rb +80 -85
  84. data/spec/kitchen/driver/dummy_spec.rb +43 -14
  85. data/spec/kitchen/driver/proxy_spec.rb +134 -0
  86. data/spec/kitchen/driver/ssh_base_spec.rb +644 -0
  87. data/spec/kitchen/driver_spec.rb +15 -15
  88. data/spec/kitchen/errors_spec.rb +309 -0
  89. data/spec/kitchen/instance_spec.rb +143 -46
  90. data/spec/kitchen/lazy_hash_spec.rb +36 -9
  91. data/spec/kitchen/loader/yaml_spec.rb +237 -226
  92. data/spec/kitchen/logger_spec.rb +419 -0
  93. data/spec/kitchen/logging_spec.rb +59 -0
  94. data/spec/kitchen/login_command_spec.rb +49 -0
  95. data/spec/kitchen/metadata_chopper_spec.rb +82 -0
  96. data/spec/kitchen/platform_spec.rb +4 -4
  97. data/spec/kitchen/provisioner/base_spec.rb +65 -125
  98. data/spec/kitchen/provisioner/chef_base_spec.rb +798 -0
  99. data/spec/kitchen/provisioner/chef_solo_spec.rb +316 -0
  100. data/spec/kitchen/provisioner/chef_zero_spec.rb +624 -0
  101. data/spec/kitchen/provisioner/shell_spec.rb +269 -0
  102. data/spec/kitchen/provisioner_spec.rb +6 -6
  103. data/spec/kitchen/shell_out_spec.rb +143 -0
  104. data/spec/kitchen/ssh_spec.rb +683 -0
  105. data/spec/kitchen/state_file_spec.rb +28 -21
  106. data/spec/kitchen/suite_spec.rb +7 -7
  107. data/spec/kitchen/util_spec.rb +68 -10
  108. data/spec/kitchen_spec.rb +107 -0
  109. data/spec/spec_helper.rb +18 -13
  110. data/support/chef-client-zero.rb +10 -9
  111. data/support/chef_helpers.sh +16 -0
  112. data/support/download_helpers.sh +109 -0
  113. data/test-kitchen.gemspec +42 -33
  114. metadata +107 -33
@@ -16,8 +16,8 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require 'kitchen/errors'
20
- require 'kitchen/logging'
19
+ require "kitchen/errors"
20
+ require "kitchen/logging"
21
21
 
22
22
  module Kitchen
23
23
 
@@ -26,24 +26,36 @@ module Kitchen
26
26
  module Chef
27
27
 
28
28
  # Chef cookbook resolver that uses Librarian-Chef and a Cheffile to
29
- # calculate # dependencies.
29
+ # calculate dependencies.
30
30
  #
31
31
  # @author Fletcher Nichol <fnichol@nichol.ca>
32
32
  class Librarian
33
33
 
34
34
  include Logging
35
35
 
36
-
36
+ # Creates a new cookbook resolver.
37
+ #
38
+ # @param cheffile [String] path to a Cheffile
39
+ # @param path [String] path in which to vendor the resulting
40
+ # cookbooks
41
+ # @param logger [Kitchen::Logger] a logger to use for output, defaults
42
+ # to `Kitchen.logger`
37
43
  def initialize(cheffile, path, logger = Kitchen.logger)
38
44
  @cheffile = cheffile
39
45
  @path = path
40
46
  @logger = logger
41
47
  end
42
48
 
49
+ # Loads the library code required to use the resolver.
50
+ #
51
+ # @param logger [Kitchen::Logger] a logger to use for output, defaults
52
+ # to `Kitchen.logger`
43
53
  def self.load!(logger = Kitchen.logger)
44
54
  load_librarian!(logger)
45
55
  end
46
56
 
57
+ # Performs the cookbook resolution and vendors the resulting cookbooks
58
+ # in the desired path.
47
59
  def resolve
48
60
  version = ::Librarian::Chef::VERSION
49
61
  info("Resolving cookbook dependencies with Librarian-Chef #{version}...")
@@ -56,12 +68,29 @@ module Kitchen
56
68
  ::Librarian::Action::Install.new(env).run
57
69
  end
58
70
 
59
- attr_reader :cheffile, :path, :logger
71
+ private
72
+
73
+ # @return [String] path to a Cheffile
74
+ # @api private
75
+ attr_reader :cheffile
76
+
77
+ # @return [String] path in which to vendor the resulting cookbooks
78
+ # @api private
79
+ attr_reader :path
80
+
81
+ # @return [Kitchen::Logger] a logger to use for output
82
+ # @api private
83
+ attr_reader :logger
60
84
 
85
+ # Load the Librarian-specific libary code.
86
+ #
87
+ # @param logger [Kitchen::Logger] the logger to use
88
+ # @raise [UserError] if the library couldn't be loaded
89
+ # @api private
61
90
  def self.load_librarian!(logger)
62
- first_load = require 'librarian/chef/environment'
63
- require 'librarian/action/resolve'
64
- require 'librarian/action/install'
91
+ first_load = require "librarian/chef/environment"
92
+ require "librarian/action/resolve"
93
+ require "librarian/action/install"
65
94
 
66
95
  version = ::Librarian::Chef::VERSION
67
96
  if first_load
@@ -70,9 +99,9 @@ module Kitchen
70
99
  logger.debug("Librarian-Chef #{version} previously loaded")
71
100
  end
72
101
  rescue LoadError => e
73
- logger.fatal("The `librarian-chef' gem is missing and must be installed" +
74
- " or cannot be properly activated. Run" +
75
- " `gem install librarian-chef` or add the following to your" +
102
+ logger.fatal("The `librarian-chef' gem is missing and must be installed" \
103
+ " or cannot be properly activated. Run" \
104
+ " `gem install librarian-chef` or add the following to your" \
76
105
  " Gemfile if you are using Bundler: `gem 'librarian-chef'`.")
77
106
  raise UserError,
78
107
  "Could not load or activate Librarian-Chef (#{e.message})"
@@ -16,13 +16,13 @@
16
16
  # See the License for the specific language governing permissions and
17
17
  # limitations under the License.
18
18
 
19
- require 'fileutils'
20
- require 'pathname'
21
- require 'json'
19
+ require "fileutils"
20
+ require "pathname"
21
+ require "json"
22
22
 
23
- require 'kitchen/provisioner/chef/berkshelf'
24
- require 'kitchen/provisioner/chef/librarian'
25
- require 'kitchen/util'
23
+ require "kitchen/provisioner/chef/berkshelf"
24
+ require "kitchen/provisioner/chef/librarian"
25
+ require "kitchen/util"
26
26
 
27
27
  module Kitchen
28
28
 
@@ -34,12 +34,17 @@ module Kitchen
34
34
  class ChefBase < Base
35
35
 
36
36
  default_config :require_chef_omnibus, true
37
- default_config :chef_omnibus_url, "https://www.getchef.com/chef/install.sh"
37
+ default_config :chef_omnibus_url, "https://www.chef.io/chef/install.sh"
38
+ default_config :chef_omnibus_root, "/opt/chef"
39
+ default_config :chef_omnibus_install_options, nil
38
40
  default_config :run_list, []
39
41
  default_config :attributes, {}
40
- default_config :cookbook_files_glob, %w[README.* metadata.{json,rb}
42
+ default_config :log_file, nil
43
+ default_config :cookbook_files_glob, %w[
44
+ README.* metadata.{json,rb}
41
45
  attributes/**/* definitions/**/* files/**/* libraries/**/*
42
- providers/**/* recipes/**/* resources/**/* templates/**/*].join(",")
46
+ providers/**/* recipes/**/* resources/**/* templates/**/*
47
+ ].join(",")
43
48
 
44
49
  default_config :data_path do |provisioner|
45
50
  provisioner.calculate_path("data")
@@ -72,79 +77,117 @@ module Kitchen
72
77
  expand_path_for :clients_path
73
78
 
74
79
  default_config :encrypted_data_bag_secret_key_path do |provisioner|
75
- provisioner.calculate_path("encrypted_data_bag_secret_key", :file)
80
+ provisioner.calculate_path("encrypted_data_bag_secret_key", :type => :file)
76
81
  end
77
82
  expand_path_for :encrypted_data_bag_secret_key_path
78
83
 
84
+ # (see Base#install_command)
79
85
  def install_command
80
86
  return unless config[:require_chef_omnibus]
81
87
 
82
- url = config[:chef_omnibus_url]
83
- flag = config[:require_chef_omnibus]
84
- version = if flag.is_a?(String) && flag != "latest"
85
- "-v #{flag.downcase}"
86
- else
87
- ""
88
- end
89
-
90
- # use Bourne (/bin/sh) as Bash does not exist on all Unix flavors
91
- <<-INSTALL.gsub(/^ {10}/, '')
92
- sh -c '
93
- #{Util.shell_helpers}
94
-
95
- should_update_chef() {
96
- case "#{flag}" in
97
- true|`chef-solo -v | cut -d " " -f 2`) return 1 ;;
98
- latest|*) return 0 ;;
99
- esac
100
- }
101
-
102
- if [ ! -d "/opt/chef" ] || should_update_chef ; then
103
- echo "-----> Installing Chef Omnibus (#{flag})"
104
- do_download #{url} /tmp/install.sh
105
- #{sudo('sh')} /tmp/install.sh #{version}
106
- fi'
107
- INSTALL
88
+ lines = [Util.shell_helpers, chef_shell_helpers, chef_install_function]
89
+ Util.wrap_command(lines.join("\n"))
108
90
  end
109
91
 
92
+ # (see Base#init_command)
110
93
  def init_command
111
- dirs = %w{cookbooks data data_bags environments roles clients}.
94
+ dirs = %w[cookbooks data data_bags environments roles clients].
112
95
  map { |dir| File.join(config[:root_path], dir) }.join(" ")
113
- "#{sudo('rm')} -rf #{dirs} ; mkdir -p #{config[:root_path]}"
96
+ lines = ["#{sudo("rm")} -rf #{dirs}", "mkdir -p #{config[:root_path]}"]
97
+
98
+ Util.wrap_command(lines.join("\n"))
114
99
  end
115
100
 
101
+ # (see Base#create_sandbox)
116
102
  def create_sandbox
117
103
  super
118
104
  prepare_json
119
105
  prepare_cache
120
106
  prepare_cookbooks
121
- prepare_data
122
- prepare_data_bags
123
- prepare_environments
124
- prepare_nodes
125
- prepare_roles
126
- prepare_clients
127
- prepare_secret
128
- end
129
-
130
- protected
131
-
107
+ prepare(:data)
108
+ prepare(:data_bags)
109
+ prepare(:environments)
110
+ prepare(:nodes)
111
+ prepare(:roles)
112
+ prepare(:clients)
113
+ prepare(
114
+ :secret,
115
+ :type => :file,
116
+ :dest_name => "encrypted_data_bag_secret",
117
+ :key_name => :encrypted_data_bag_secret_key_path
118
+ )
119
+ end
120
+
121
+ private
122
+
123
+ # Load cookbook dependency resolver code, if required.
124
+ #
125
+ # (see Base#load_needed_dependencies!)
132
126
  def load_needed_dependencies!
133
- if File.exists?(berksfile)
127
+ if File.exist?(berksfile)
134
128
  debug("Berksfile found at #{berksfile}, loading Berkshelf")
135
129
  Chef::Berkshelf.load!(logger)
136
- elsif File.exists?(cheffile)
130
+ elsif File.exist?(cheffile)
137
131
  debug("Cheffile found at #{cheffile}, loading Librarian-Chef")
138
132
  Chef::Librarian.load!(logger)
139
133
  end
140
134
  end
141
135
 
136
+ # Returns shell code with chef-related functions.
137
+ #
138
+ # @return [String] shell code
139
+ # @api private
140
+ def chef_shell_helpers
141
+ IO.read(File.join(
142
+ File.dirname(__FILE__), %w[.. .. .. support chef_helpers.sh]
143
+ ))
144
+ end
145
+
146
+ # Generates the shell code to conditionally install a Chef Omnibus
147
+ # package onto an instance.
148
+ #
149
+ # @return [String] shell code
150
+ # @api private
151
+ def chef_install_function
152
+ version = config[:require_chef_omnibus].to_s.downcase
153
+ pretty_version = case version
154
+ when "true" then "install only if missing"
155
+ when "latest" then "always install latest version"
156
+ else version
157
+ end
158
+ install_flags = %w[latest true].include?(version) ? "" : "-v #{version}"
159
+ if config[:chef_omnibus_install_options]
160
+ install_flags += config[:chef_omnibus_install_options]
161
+ end
162
+
163
+ <<-INSTALL.gsub(/^ {10}/, "")
164
+ if should_update_chef "#{config[:chef_omnibus_root]}" "#{version}" ; then
165
+ echo "-----> Installing Chef Omnibus (#{pretty_version})"
166
+ do_download #{config[:chef_omnibus_url]} /tmp/install.sh
167
+ #{sudo("sh")} /tmp/install.sh #{install_flags}
168
+ else
169
+ echo "-----> Chef Omnibus installation detected (#{pretty_version})"
170
+ fi
171
+ INSTALL
172
+ end
173
+
174
+ # Generates a rendered client.rb/solo.rb/knife.rb formatted file as a
175
+ # String.
176
+ #
177
+ # @param data [Hash] a key/value pair hash of configuration
178
+ # @return [String] a rendered Chef config file as a String
179
+ # @api private
142
180
  def format_config_file(data)
143
181
  data.each.map { |attr, value|
144
- [attr, (value.is_a?(Array) ? value.to_s : %{"#{value}"})].join(" ")
182
+ [attr, value.inspect].join(" ")
145
183
  }.join("\n")
146
184
  end
147
185
 
186
+ # Generates a Hash with default values for a solo.rb or client.rb Chef
187
+ # configuration file.
188
+ #
189
+ # @return [Hash] a configuration hash
190
+ # @api private
148
191
  def default_config_rb
149
192
  root = config[:root_path]
150
193
 
@@ -163,105 +206,78 @@ module Kitchen
163
206
  :validation_key => "#{root}/validation.pem",
164
207
  :client_key => "#{root}/client.pem",
165
208
  :chef_server_url => "http://127.0.0.1:8889",
166
- :encrypted_data_bag_secret => "#{root}/encrypted_data_bag_secret",
209
+ :encrypted_data_bag_secret => "#{root}/encrypted_data_bag_secret"
167
210
  }
168
211
  end
169
212
 
170
- def prepare_json
171
- dna = config[:attributes].merge({ :run_list => config[:run_list] })
172
-
173
- File.open(File.join(sandbox_path, "dna.json"), "wb") do |file|
174
- file.write(dna.to_json)
213
+ # Prepares a generic Chef component source directory or file for
214
+ # inclusion in the sandbox path. These components might includes nodes,
215
+ # roles, etc.
216
+ #
217
+ # @param component [Symbol,String] a component name such as `:node`
218
+ # @param opts [Hash] optional configuration
219
+ # @option opts [Symbol] :type whether the component is a directory or
220
+ # file (default: `:directory`)
221
+ # @option opts [Symbol] :key_name the key name in the config hash from
222
+ # which to pull the source path (default: `"#{component}_path"`)
223
+ # @option opts [String] :dest_name the destination file or directory
224
+ # basename in the sandbox path (default: `component.to_s`)
225
+ # @api private
226
+ def prepare(component, opts = {})
227
+ opts = { :type => :directory }.merge(opts)
228
+ key_name = opts.fetch(:key_name, "#{component}_path")
229
+ src = config[key_name.to_sym]
230
+ return if src.nil?
231
+
232
+ info("Preparing #{component}")
233
+ debug("Using #{component} from #{src}")
234
+
235
+ dest = File.join(sandbox_path, opts.fetch(:dest_name, component.to_s))
236
+
237
+ case opts[:type]
238
+ when :directory
239
+ FileUtils.mkdir_p(dest)
240
+ FileUtils.cp_r(Dir.glob("#{src}/*"), dest)
241
+ when :file
242
+ FileUtils.mkdir_p(File.dirname(dest))
243
+ FileUtils.cp_r(src, dest)
175
244
  end
176
245
  end
177
246
 
178
- def prepare_data
179
- return unless data
180
-
181
- info("Preparing data")
182
- debug("Using data from #{data}")
183
-
184
- tmpdata_dir = File.join(sandbox_path, "data")
185
- FileUtils.mkdir_p(tmpdata_dir)
186
- FileUtils.cp_r(Dir.glob("#{data}/*"), tmpdata_dir)
187
- end
188
-
189
- def prepare_data_bags
190
- return unless data_bags
191
-
192
- info("Preparing data bags")
193
- debug("Using data bags from #{data_bags}")
194
-
195
- tmpbags_dir = File.join(sandbox_path, "data_bags")
196
- FileUtils.mkdir_p(tmpbags_dir)
197
- FileUtils.cp_r(Dir.glob("#{data_bags}/*"), tmpbags_dir)
198
- end
199
-
200
- def prepare_roles
201
- return unless roles
202
-
203
- info("Preparing roles")
204
- debug("Using roles from #{roles}")
205
-
206
- tmproles_dir = File.join(sandbox_path, "roles")
207
- FileUtils.mkdir_p(tmproles_dir)
208
- FileUtils.cp_r(Dir.glob("#{roles}/*"), tmproles_dir)
209
- end
210
-
211
- def prepare_clients
212
- return unless clients
213
-
214
- info("Preparing clients")
215
- debug("Using roles from #{clients}")
216
-
217
- tmpclients_dir = File.join(sandbox_path, "clients")
218
- FileUtils.mkdir_p(tmpclients_dir)
219
- FileUtils.cp_r(Dir.glob("#{clients}/*"), tmpclients_dir)
220
- end
221
-
222
- def prepare_nodes
223
- return unless nodes
224
-
225
- info("Preparing nodes")
226
- debug("Using nodes from #{nodes}")
227
-
228
- tmpnodes_dir = File.join(sandbox_path, "nodes")
229
- FileUtils.mkdir_p(tmpnodes_dir)
230
- FileUtils.cp_r(Dir.glob("#{nodes}/*"), tmpnodes_dir)
231
- end
232
-
233
- def prepare_environments
234
- return unless environments
235
-
236
- info("Preparing environments")
237
- debug("Using environments from #{environments}")
238
-
239
- tmpenvs_dir = File.join(sandbox_path, "environments")
240
- FileUtils.mkdir_p(tmpenvs_dir)
241
- FileUtils.cp_r(Dir.glob("#{environments}/*"), tmpenvs_dir)
242
- end
243
-
244
- def prepare_secret
245
- return unless secret
247
+ # Prepares a Chef JSON file, sometimes called a dna.json or
248
+ # first-boot.json, for inclusion in the sandbox path.
249
+ #
250
+ # @api private
251
+ def prepare_json
252
+ dna = config[:attributes].merge(:run_list => config[:run_list])
246
253
 
247
- info("Preparing encrypted data bag secret")
248
- debug("Using secret from #{secret}")
254
+ info("Preparing dna.json")
255
+ debug("Creating dna.json from #{dna.inspect}")
249
256
 
250
- FileUtils.cp_r(secret, File.join(sandbox_path, "encrypted_data_bag_secret"))
257
+ File.open(File.join(sandbox_path, "dna.json"), "wb") do |file|
258
+ file.write(dna.to_json)
259
+ end
251
260
  end
252
261
 
262
+ # Prepares a cache directory for inclusion in the sandbox path.
263
+ #
264
+ # @api private
253
265
  def prepare_cache
254
266
  FileUtils.mkdir_p(File.join(sandbox_path, "cache"))
255
267
  end
256
268
 
269
+ # Prepares Chef cookbooks for inclusion in the sandbox path.
270
+ #
271
+ # @api private
257
272
  def prepare_cookbooks
258
- if File.exists?(berksfile)
273
+ if File.exist?(berksfile)
259
274
  resolve_with_berkshelf
260
- elsif File.exists?(cheffile)
275
+ elsif File.exist?(cheffile)
261
276
  resolve_with_librarian
277
+ cp_site_cookbooks if File.directory?(site_cookbooks_dir)
262
278
  elsif File.directory?(cookbooks_dir)
263
279
  cp_cookbooks
264
- elsif File.exists?(metadata_rb)
280
+ elsif File.exist?(metadata_rb)
265
281
  cp_this_cookbook
266
282
  else
267
283
  make_fake_cookbook
@@ -270,79 +286,86 @@ module Kitchen
270
286
  filter_only_cookbook_files
271
287
  end
272
288
 
289
+ # Removes all non-cookbook files in the sandbox path.
290
+ #
291
+ # @api private
273
292
  def filter_only_cookbook_files
274
293
  info("Removing non-cookbook files before transfer")
275
294
  FileUtils.rm(all_files_in_cookbooks - only_cookbook_files)
276
295
  end
277
296
 
297
+ # Generates a list of all files in the cookbooks directory in the
298
+ # sandbox path.
299
+ #
300
+ # @return [Array<String>] an array of absolute paths to files
301
+ # @api private
278
302
  def all_files_in_cookbooks
279
303
  Dir.glob(File.join(tmpbooks_dir, "**/*"), File::FNM_DOTMATCH).
280
- select { |fn| File.file?(fn) && ! %w{. ..}.include?(fn) }
304
+ select { |fn| File.file?(fn) && ! %w[. ..].include?(fn) }
281
305
  end
282
306
 
307
+ # Generates a list of all typical cookbook files needed in a Chef run,
308
+ # located in the cookbooks directory in the sandbox path.
309
+ #
310
+ # @return [Array<String>] an array of absolute paths to files
311
+ # @api private
283
312
  def only_cookbook_files
284
313
  glob = File.join(tmpbooks_dir, "*", "{#{config[:cookbook_files_glob]}}")
285
314
 
286
315
  Dir.glob(glob, File::FNM_DOTMATCH).
287
- select { |fn| File.file?(fn) && ! %w{. ..}.include?(fn) }
316
+ select { |fn| File.file?(fn) && ! %w[. ..].include?(fn) }
288
317
  end
289
318
 
319
+ # @return [String] an absolute path to a Berksfile, relative to the
320
+ # kitchen root
321
+ # @api private
290
322
  def berksfile
291
323
  File.join(config[:kitchen_root], "Berksfile")
292
324
  end
293
325
 
326
+ # @return [String] an absolute path to a Cheffile, relative to the
327
+ # kitchen root
328
+ # @api private
294
329
  def cheffile
295
330
  File.join(config[:kitchen_root], "Cheffile")
296
331
  end
297
332
 
333
+ # @return [String] an absolute path to a metadata.rb, relative to the
334
+ # kitchen root
335
+ # @api private
298
336
  def metadata_rb
299
337
  File.join(config[:kitchen_root], "metadata.rb")
300
338
  end
301
339
 
340
+ # @return [String] an absolute path to a cookbooks/ directory, relative
341
+ # to the kitchen root
342
+ # @api private
302
343
  def cookbooks_dir
303
344
  File.join(config[:kitchen_root], "cookbooks")
304
345
  end
305
346
 
347
+ # @return [String] an absolute path to a site-cookbooks/ directory,
348
+ # relative to the kitchen root
349
+ # @api private
306
350
  def site_cookbooks_dir
307
351
  File.join(config[:kitchen_root], "site-cookbooks")
308
352
  end
309
353
 
310
- def data_bags
311
- config[:data_bags_path]
312
- end
313
-
314
- def roles
315
- config[:roles_path]
316
- end
317
-
318
- def clients
319
- config[:clients_path]
320
- end
321
-
322
- def nodes
323
- config[:nodes_path]
324
- end
325
-
326
- def data
327
- config[:data_path]
328
- end
329
-
330
- def environments
331
- config[:environments_path]
332
- end
333
-
334
- def secret
335
- config[:encrypted_data_bag_secret_key_path]
336
- end
337
-
354
+ # @return [String] an absolute path to a cookbooks/ directory in the
355
+ # sandbox path
356
+ # @api private
338
357
  def tmpbooks_dir
339
358
  File.join(sandbox_path, "cookbooks")
340
359
  end
341
360
 
361
+ # @return [String] an absolute path to a site cookbooks directory in the
362
+ # sandbox path
363
+ # @api private
342
364
  def tmpsitebooks_dir
343
365
  File.join(sandbox_path, "cookbooks")
344
366
  end
345
367
 
368
+ # Copies a cookbooks/ directory into the sandbox path.
346
369
  def cp_cookbooks
347
370
  info("Preparing cookbooks from project directory")
348
371
  debug("Using cookbooks from #{cookbooks_dir}")
@@ -351,9 +374,12 @@ module Kitchen
351
374
  FileUtils.cp_r(File.join(cookbooks_dir, "."), tmpbooks_dir)
352
375
 
353
376
  cp_site_cookbooks if File.directory?(site_cookbooks_dir)
354
- cp_this_cookbook if File.exists?(metadata_rb)
377
+ cp_this_cookbook if File.exist?(metadata_rb)
355
378
  end
356
379
 
380
+ # Copies a site-cookbooks/ directory into the sandbox path.
381
+ #
382
+ # @api private
357
383
  def cp_site_cookbooks
358
384
  info("Preparing site-cookbooks from project directory")
359
385
  debug("Using cookbooks from #{site_cookbooks_dir}")
@@ -362,12 +388,16 @@ module Kitchen
362
388
  FileUtils.cp_r(File.join(site_cookbooks_dir, "."), tmpsitebooks_dir)
363
389
  end
364
390
 
391
+ # Copies the current project, assumed to be a Chef cookbook into the
392
+ # sandbox path.
393
+ #
394
+ # @api private
365
395
  def cp_this_cookbook
366
396
  info("Preparing current project directory as a cookbook")
367
397
  debug("Using metadata.rb from #{metadata_rb}")
368
398
 
369
- cb_name = MetadataChopper.extract(metadata_rb).first or raise(UserError,
370
- "The metadata.rb does not define the 'name' key." +
399
+ cb_name = MetadataChopper.extract(metadata_rb).first || raise(UserError,
400
+ "The metadata.rb does not define the 'name' key." \
371
401
  " Please add: `name '<cookbook_name>'` to metadata.rb and retry")
372
402
 
373
403
  cb_path = File.join(tmpbooks_dir, cb_name)
@@ -378,23 +408,32 @@ module Kitchen
378
408
  FileUtils.cp_r(glob, cb_path)
379
409
  end
380
410
 
411
+ # Creates a minimal, no-op cookbook in the sandbox path.
412
+ #
413
+ # @api private
381
414
  def make_fake_cookbook
382
- info("Berksfile, Cheffile, cookbooks/, or metadata.rb not found " +
415
+ info("Berksfile, Cheffile, cookbooks/, or metadata.rb not found " \
383
416
  "so Chef will run with effectively no cookbooks. Is this intended?")
384
417
  name = File.basename(config[:kitchen_root])
385
418
  fake_cb = File.join(tmpbooks_dir, name)
386
419
  FileUtils.mkdir_p(fake_cb)
387
420
  File.open(File.join(fake_cb, "metadata.rb"), "wb") do |file|
388
- file.write(%{name "#{name}\n"})
421
+ file.write(%{name "#{name}"\n})
389
422
  end
390
423
  end
391
424
 
425
+ # Performs a Berkshelf cookbook resolution inside a common mutex.
426
+ #
427
+ # @api private
392
428
  def resolve_with_berkshelf
393
429
  Kitchen.mutex.synchronize do
394
430
  Chef::Berkshelf.new(berksfile, tmpbooks_dir, logger).resolve
395
431
  end
396
432
  end
397
433
 
434
+ # Performs a Librarin-Chef cookbook resolution inside a common mutex.
435
+ #
436
+ # @api private
398
437
  def resolve_with_librarian
399
438
  Kitchen.mutex.synchronize do
400
439
  Chef::Librarian.new(cheffile, tmpbooks_dir, logger).resolve