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,76 @@
1
+ #
2
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
+ #
4
+ # Copyright (C) 2012, Fletcher Nichol
5
+ # Copyright (C) 2018, Chef Software
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_relative "errors"
20
+ require_relative "util"
21
+
22
+ module Kitchen
23
+ module Plugin
24
+ # Returns an instance of a plugin given a type, name, and config.
25
+ #
26
+ # @param type [Module] a Kitchen::<Module> of one of the plugin types
27
+ # (Driver, Provisioner, Transport, Verifier)
28
+ # @param plugin [String] a plugin name, which will be constantized
29
+ # @param config [Hash] a configuration hash to initialize the plugin
30
+ # @return [Kitchen::<Module>::Base] a plugin instance
31
+ # @raise [ClientError] if a plugin instance could not be created
32
+ # @raise [UserError] if the plugin's dependencies could not be met
33
+ def self.load(type, plugin, config)
34
+ type_name = Kitchen::Util.snake_case(type.name.split("::").last)
35
+ first_load = require("kitchen/#{type_name}/#{plugin}")
36
+
37
+ str_const = Kitchen::Util.camel_case(plugin)
38
+ klass = type.const_get(str_const)
39
+ object = klass.new(config)
40
+ object.verify_dependencies if first_load
41
+ object
42
+ rescue UserError
43
+ raise
44
+ rescue NameError => e
45
+ raise ClientError, "Could not load the '#{plugin}' #{type_name}. Error: #{e.message}"
46
+ rescue LoadError => e
47
+ available_plugins = plugins_available(type_name)
48
+ error_message = if available_plugins.include?(plugin)
49
+ e.message
50
+ else
51
+ " Did you mean: #{available_plugins.join(", ")} ?" \
52
+ " Please ensure that your #{type_name} is installed as a gem or included" \
53
+ " in your Gemfile if using Bundler."
54
+ end
55
+ raise ClientError, "Could not load the '#{plugin}' #{type_name} from the load path." + error_message
56
+ end
57
+
58
+ # given a type of plugin, searches the Ruby load path for plugins of that
59
+ # type based on the path+naming convention that plugin loading is based upon
60
+ #
61
+ # @param plugin_type [String] the name of a plugin type (e.g. driver,
62
+ # provisioner, transport, verifier)
63
+ # @return [Array<String>] a collection of Ruby filenames that are probably
64
+ # plugins of the given type
65
+ def self.plugins_available(plugin_type)
66
+ $LOAD_PATH.map { |load_path| Dir[File.expand_path("kitchen/#{plugin_type}/*.rb", load_path)] }
67
+ .reject(&:empty?)
68
+ .flatten
69
+ .uniq
70
+ .select { |plugin_path| File.readlines(plugin_path).grep(/^\s*class \w* </).any? }
71
+ .map { |plugin_path| File.basename(plugin_path).gsub(/\.rb$/, "") }
72
+ .reject { |plugin_name| plugin_name == "base" }
73
+ .sort
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,60 @@
1
+ #
2
+ # Author:: Fletcher Nichol (<fnichol@nichol.ca>)
3
+ #
4
+ # Copyright (C) 2014, 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
+ module Kitchen
19
+ module Plugin
20
+ class Base
21
+ class << self
22
+ # @return [Array<Symbol>] an array of action method names that cannot
23
+ # be run concurrently and must be run in serial via a shared mutex
24
+ attr_reader :serial_actions
25
+ end
26
+
27
+ # Registers certain driver actions that cannot be safely run concurrently
28
+ # in threads across multiple instances. Typically this might be used
29
+ # for create or destroy actions that use an underlying resource that
30
+ # cannot be used at the same time.
31
+ #
32
+ # A shared mutex for this driver object will be used to synchronize all
33
+ # registered methods.
34
+ #
35
+ # @example a single action method that cannot be run concurrently
36
+ #
37
+ # no_parallel_for :create
38
+ #
39
+ # @example multiple action methods that cannot be run concurrently
40
+ #
41
+ # no_parallel_for :create, :destroy
42
+ #
43
+ # @param methods [Array<Symbol>] one or more actions as symbols
44
+ # @raise [ClientError] if any method is not a valid action method name
45
+ def self.no_parallel_for(*methods)
46
+ action_methods = %i{create setup converge verify destroy}
47
+
48
+ Array(methods).each do |meth|
49
+ next if action_methods.include?(meth)
50
+
51
+ raise ClientError, "##{meth} is not a valid no_parallel_for method"
52
+ end
53
+
54
+ @serial_actions ||= []
55
+ @serial_actions += methods
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,269 @@
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_relative "../configurable"
19
+ require_relative "../errors"
20
+ require_relative "../logging"
21
+ require_relative "../plugin_base"
22
+
23
+ module Kitchen
24
+ module Provisioner
25
+ # Base class for a provisioner.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ class Base < Kitchen::Plugin::Base
29
+ include Configurable
30
+ include Logging
31
+
32
+ default_config :http_proxy, nil
33
+ default_config :https_proxy, nil
34
+ default_config :ftp_proxy, nil
35
+
36
+ default_config :retry_on_exit_code, []
37
+ default_config :max_retries, 1
38
+ default_config :wait_for_retry, 30
39
+
40
+ default_config :root_path do |provisioner|
41
+ provisioner.windows_os? ? '$env:TEMP\\kitchen' : "/tmp/kitchen"
42
+ end
43
+
44
+ default_config :sudo do |provisioner|
45
+ provisioner.windows_os? ? nil : true
46
+ end
47
+
48
+ default_config :sudo_command do |provisioner|
49
+ provisioner.windows_os? ? nil : "sudo -E"
50
+ end
51
+
52
+ default_config :command_prefix, nil
53
+
54
+ default_config :uploads, {}
55
+ default_config :downloads, {}
56
+
57
+ expand_path_for :test_base_path
58
+
59
+ # Constructs a new provisioner by providing a configuration hash.
60
+ #
61
+ # @param config [Hash] initial provided configuration
62
+ def initialize(config = {})
63
+ init_config(config)
64
+ end
65
+
66
+ # Runs the provisioner on the instance.
67
+ #
68
+ # @param state [Hash] mutable instance state
69
+ # @raise [ActionFailed] if the action could not be completed
70
+ # rubocop:disable Metrics/AbcSize
71
+ def call(state)
72
+ create_sandbox
73
+ sandbox_dirs = Util.list_directory(sandbox_path)
74
+
75
+ instance.transport.connection(state) do |conn|
76
+ config[:uploads].to_h.each do |locals, remote|
77
+ debug("Uploading #{Array(locals).join(", ")} to #{remote}")
78
+ conn.upload(locals.to_s, remote)
79
+ end
80
+ conn.execute(install_command)
81
+ conn.execute(init_command)
82
+ info("Transferring files to #{instance.to_str} using rsync")
83
+ conn.rsyn_chef_repo(sandbox_path, config[:root_path])
84
+ debug("Transfer complete")
85
+ conn.execute(prepare_command)
86
+ conn.execute_with_retry(
87
+ run_command,
88
+ config[:retry_on_exit_code],
89
+ config[:max_retries],
90
+ config[:wait_for_retry]
91
+ )
92
+ info("Downloading files from #{instance.to_str}")
93
+ config[:downloads].to_h.each do |remotes, local|
94
+ debug("Downloading #{Array(remotes).join(", ")} to #{local}")
95
+ conn.download(remotes, local)
96
+ end
97
+ debug("Download complete")
98
+ end
99
+ rescue Kitchen::Transport::TransportFailed => ex
100
+ raise ActionFailed, ex.message
101
+ ensure
102
+ cleanup_sandbox
103
+ end
104
+
105
+ # Check system and configuration for common errors.
106
+ #
107
+ # @param state [Hash] mutable instance state
108
+ # @returns [Boolean] Return true if a problem is found.
109
+ def doctor(state)
110
+ false
111
+ end
112
+
113
+ # Certain products that Test Kitchen uses to provision require accepting
114
+ # a license to use. Overwrite this method in the specific provisioner
115
+ # to implement this check.
116
+ def check_license; end
117
+
118
+ # Generates a command string which will install and configure the
119
+ # provisioner software on an instance. If no work is required, then `nil`
120
+ # will be returned.
121
+ #
122
+ # @return [String] a command string
123
+ def install_command; end
124
+
125
+ # Generates a command string which will perform any data initialization
126
+ # or configuration required after the provisioner software is installed
127
+ # but before the sandbox has been transferred to the instance. If no work
128
+ # is required, then `nil` will be returned.
129
+ #
130
+ # @return [String] a command string
131
+ def init_command; end
132
+
133
+ # Generates a command string which will perform any commands or
134
+ # configuration required just before the main provisioner run command but
135
+ # after the sandbox has been transferred to the instance. If no work is
136
+ # required, then `nil` will be returned.
137
+ #
138
+ # @return [String] a command string
139
+ def prepare_command; end
140
+
141
+ # Generates a command string which will invoke the main provisioner
142
+ # command on the prepared instance. If no work is required, then `nil`
143
+ # will be returned.
144
+ #
145
+ # @return [String] a command string
146
+ def run_command; end
147
+
148
+ # Creates a temporary directory on the local workstation into which
149
+ # provisioner related files and directories can be copied or created. The
150
+ # contents of this directory will be copied over to the instance before
151
+ # invoking the provisioner's run command. After this method completes, it
152
+ # is expected that the contents of the sandbox is complete and ready for
153
+ # copy to the remote instance.
154
+ #
155
+ # **Note:** any subclasses would be well advised to call super first when
156
+ # overriding this method, for example:
157
+ #
158
+ # @example overriding `#create_sandbox`
159
+ #
160
+ # class MyProvisioner < Kitchen::Provisioner::Base
161
+ # def create_sandbox
162
+ # super
163
+ # # any further file copies, preparations, etc.
164
+ # end
165
+ # end
166
+ def create_sandbox
167
+ @sandbox_path = Dir.mktmpdir("#{instance.name}-sandbox-")
168
+ File.chmod(0755, sandbox_path)
169
+ info("Preparing files for transfer")
170
+ debug("Creating local sandbox in #{sandbox_path}")
171
+ end
172
+
173
+ # Returns the absolute path to the sandbox directory or raises an
174
+ # exception if `#create_sandbox` has not yet been called.
175
+ #
176
+ # @return [String] the absolute path to the sandbox directory
177
+ # @raise [ClientError] if the sandbox directory has no yet been created
178
+ # by calling `#create_sandbox`
179
+ def sandbox_path
180
+ @sandbox_path ||= raise ClientError, "Sandbox directory has not yet " \
181
+ "been created. Please run #{self.class}#create_sandox before " \
182
+ "trying to access the path."
183
+ end
184
+
185
+ # Deletes the sandbox path. Without calling this method, the sandbox path
186
+ # will persist after the process terminates. In other words, cleanup is
187
+ # explicit. This method is safe to call multiple times.
188
+ def cleanup_sandbox
189
+ return if sandbox_path.nil?
190
+
191
+ debug("Cleaning up local sandbox in #{sandbox_path}")
192
+ FileUtils.rmtree(sandbox_path)
193
+ end
194
+
195
+ # Sets the API version for this provisioner. If the provisioner does not
196
+ # set this value, then `nil` will be used and reported.
197
+ #
198
+ # Sets the API version for this provisioner
199
+ #
200
+ # @example setting an API version
201
+ #
202
+ # module Kitchen
203
+ # module Provisioner
204
+ # class NewProvisioner < Kitchen::Provisioner::Base
205
+ #
206
+ # kitchen_provisioner_api_version 2
207
+ #
208
+ # end
209
+ # end
210
+ # end
211
+ #
212
+ # @param version [Integer,String] a version number
213
+ #
214
+ def self.kitchen_provisioner_api_version(version)
215
+ @api_version = version
216
+ end
217
+
218
+ private
219
+
220
+ # Builds a complete command given a variables String preamble and a file
221
+ # containing shell code.
222
+ #
223
+ # @param vars [String] shell variables, as a String
224
+ # @param file [String] file basename (without extension) containing
225
+ # shell code
226
+ # @return [String] command
227
+ # @api private
228
+ def shell_code_from_file(vars, file)
229
+ src_file = File.join(
230
+ File.dirname(__FILE__),
231
+ %w{.. .. .. support},
232
+ file + (powershell_shell? ? ".ps1" : ".sh")
233
+ )
234
+
235
+ wrap_shell_code([vars, "", IO.read(src_file)].join("\n"))
236
+ end
237
+
238
+ # Conditionally prefixes a command with a sudo command.
239
+ #
240
+ # @param command [String] command to be prefixed
241
+ # @return [String] the command, conditionally prefixed with sudo
242
+ # @api private
243
+ def sudo(script)
244
+ "#{sudo_command} #{script}".lstrip
245
+ end
246
+
247
+ # Returns the sudo command to use or empty string if sudo is not configured
248
+ #
249
+ # @return [String] the sudo command if sudo config is true
250
+ # @api private
251
+ def sudo_command
252
+ config[:sudo] ? config[:sudo_command].to_s : ""
253
+ end
254
+
255
+ # Conditionally prefixes a command with a command prefix.
256
+ # This should generally be done after a command has been
257
+ # conditionally prefixed by #sudo as certain platforms, such as
258
+ # Cisco Nexus, require all commands to be run with a prefix to
259
+ # obtain outbound network access.
260
+ #
261
+ # @param command [String] command to be prefixed
262
+ # @return [String] the command, conditionally prefixed with the configured prefix
263
+ # @api private
264
+ def prefix_command(script)
265
+ config[:command_prefix] ? "#{config[:command_prefix]} #{script}" : script
266
+ end
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,116 @@
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_relative "../../errors"
19
+ require_relative "../../logging"
20
+
21
+ module Kitchen
22
+ module Provisioner
23
+ module Chef
24
+ # Chef cookbook resolver that uses Berkshelf and a Berksfile to calculate
25
+ # dependencies.
26
+ #
27
+ # @author Fletcher Nichol <fnichol@nichol.ca>
28
+ class Berkshelf
29
+ include Logging
30
+
31
+ # Creates a new cookbook resolver.
32
+ #
33
+ # @param berksfile [String] path to a Berksfile
34
+ # @param path [String] path in which to vendor the resulting
35
+ # cookbooks
36
+ # @param logger [Kitchen::Logger] a logger to use for output, defaults
37
+ # to `Kitchen.logger`
38
+ def initialize(berksfile, path, logger: Kitchen.logger, always_update: false)
39
+ @berksfile = berksfile
40
+ @path = path
41
+ @logger = logger
42
+ @always_update = always_update
43
+ end
44
+
45
+ # Loads the library code required to use the resolver.
46
+ #
47
+ # @param logger [Kitchen::Logger] a logger to use for output, defaults
48
+ # to `Kitchen.logger`
49
+ def self.load!(logger: Kitchen.logger)
50
+ load_berkshelf!(logger)
51
+ end
52
+
53
+ # Performs the cookbook resolution and vendors the resulting cookbooks
54
+ # in the desired path.
55
+ def resolve
56
+ version = ::Berkshelf::VERSION
57
+ info("Resolving cookbook dependencies with Berkshelf #{version}...")
58
+ debug("Using Berksfile from #{berksfile}")
59
+
60
+ ::Berkshelf.ui.mute do
61
+ berksfile_obj = ::Berkshelf::Berksfile.from_file(berksfile)
62
+ berksfile_obj.update if always_update && berksfile_obj.lockfile.present?
63
+ # Berkshelf requires the directory to not exist
64
+ FileUtils.rm_rf(path)
65
+ berksfile_obj.vendor(path)
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # @return [String] path to a Berksfile
72
+ # @api private
73
+ attr_reader :berksfile
74
+
75
+ # @return [String] path in which to vendor the resulting cookbooks
76
+ # @api private
77
+ attr_reader :path
78
+
79
+ # @return [Kitchen::Logger] a logger to use for output
80
+ # @api private
81
+ attr_reader :logger
82
+
83
+ # @return [Boolean] If true, always update cookbooks in Berkshelf.
84
+ # @api private
85
+ attr_reader :always_update
86
+
87
+ class << self
88
+ private
89
+
90
+ # Load the Berkshelf-specific libary code.
91
+ #
92
+ # @param logger [Kitchen::Logger] the logger to use
93
+ # @raise [UserError] if the library couldn't be loaded
94
+ # @api private
95
+ def load_berkshelf!(logger)
96
+ first_load = require "berkshelf"
97
+
98
+ version = ::Berkshelf::VERSION
99
+ if first_load
100
+ logger.debug("Berkshelf #{version} library loaded")
101
+ else
102
+ logger.debug("Berkshelf #{version} previously loaded")
103
+ end
104
+ rescue LoadError => e
105
+ logger.fatal("The `berkshelf' gem is missing and must be installed" \
106
+ " or cannot be properly activated. Run" \
107
+ " `gem install berkshelf` or add the following to your" \
108
+ " Gemfile if you are using Bundler: `gem 'berkshelf'`.")
109
+ raise UserError,
110
+ "Could not load or activate Berkshelf (#{e.message})"
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end