berkshelf 0.3.7 → 0.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/.gitignore +2 -1
  2. data/README.md +8 -0
  3. data/Thorfile +2 -2
  4. data/berkshelf.gemspec +1 -1
  5. data/features/install.feature +102 -2
  6. data/features/lockfile.feature +1 -1
  7. data/features/step_definitions/chef_server_steps.rb +8 -0
  8. data/features/step_definitions/cli_steps.rb +1 -1
  9. data/features/step_definitions/filesystem_steps.rb +12 -0
  10. data/features/support/env.rb +8 -10
  11. data/features/update.feature +1 -1
  12. data/lib/berkshelf.rb +19 -1
  13. data/lib/berkshelf/berksfile.rb +18 -6
  14. data/lib/berkshelf/cli.rb +3 -8
  15. data/lib/berkshelf/cookbook_source.rb +108 -23
  16. data/lib/berkshelf/cookbook_source/chef_api_location.rb +256 -0
  17. data/lib/berkshelf/cookbook_source/git_location.rb +14 -2
  18. data/lib/berkshelf/cookbook_source/location.rb +119 -2
  19. data/lib/berkshelf/cookbook_source/path_location.rb +6 -1
  20. data/lib/berkshelf/cookbook_source/site_location.rb +36 -105
  21. data/lib/berkshelf/cookbook_store.rb +7 -12
  22. data/lib/berkshelf/dsl.rb +1 -1
  23. data/lib/berkshelf/errors.rb +2 -0
  24. data/lib/berkshelf/init_generator.rb +6 -6
  25. data/lib/berkshelf/resolver.rb +8 -34
  26. data/lib/berkshelf/uploader.rb +7 -7
  27. data/lib/berkshelf/version.rb +1 -1
  28. data/spec/fixtures/reset.pem +27 -0
  29. data/spec/knife.rb.sample +12 -0
  30. data/spec/spec_helper.rb +4 -1
  31. data/spec/support/chef_api.rb +32 -0
  32. data/spec/support/knife.rb +18 -0
  33. data/spec/unit/berkshelf/cookbook_source/chef_api_location_spec.rb +243 -0
  34. data/spec/unit/berkshelf/cookbook_source/git_location_spec.rb +2 -2
  35. data/spec/unit/berkshelf/cookbook_source/location_spec.rb +130 -2
  36. data/spec/unit/berkshelf/cookbook_source/path_location_spec.rb +2 -2
  37. data/spec/unit/berkshelf/cookbook_source/site_location_spec.rb +22 -105
  38. data/spec/unit/berkshelf/cookbook_source_spec.rb +140 -71
  39. data/spec/unit/berkshelf/cookbook_store_spec.rb +6 -6
  40. data/spec/unit/berkshelf/resolver_spec.rb +9 -30
  41. data/spec/unit/berkshelf/uploader_spec.rb +1 -10
  42. data/spec/unit/berkshelf_spec.rb +21 -1
  43. metadata +19 -15
  44. data/features/config.sample.yml +0 -3
data/.gitignore CHANGED
@@ -15,6 +15,7 @@ tmp
15
15
  *.tar*
16
16
  \#*
17
17
  .DS_Store
18
- /spec/fixtures/vcr_cassettes/*
18
+ /spec/knife.rb
19
+ /spec/fixtures/vcr_cassettes
19
20
  /features/config.yml
20
21
  *.sw[op]
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # Berkshelf
2
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/RiotGames/berkshelf)
2
3
 
3
4
  Manage a Cookbook or an Application's Cookbook dependencies
4
5
 
@@ -33,8 +34,15 @@ Bundler will install all gems and their dependencies required for testing and de
33
34
 
34
35
  ### Running unit (RSpec) and acceptance (Cucumber) tests
35
36
 
37
+ Simply copy the `spec/knife.rb.sample` to `spec/knife.rb`, and point it at a
38
+ chef server. Berkshelf tests may upload and destroy cookbooks on your chef
39
+ server, so be sure to configure a server safe for this task.
40
+
36
41
  $ bundle exec guard start
37
42
 
43
+ See [here](https://github.com/tdegrunt/vagrant-chef-server-bootstrap) for a
44
+ quick way to get a testing chef server up.
45
+
38
46
  # Authors and Contributors
39
47
 
40
48
  * Josiah Kiehl (<josiah@skirmisher.net>)
data/Thorfile CHANGED
@@ -33,7 +33,7 @@ class Default < Thor
33
33
 
34
34
  desc "cucumber", "Run Cucumber features"
35
35
  def cucumber
36
- exec "cucumber --color --format=progress"
36
+ exec "cucumber --color --format progress --tags ~@no_run"
37
37
  end
38
38
 
39
39
  class VCR < Thor
@@ -41,7 +41,7 @@ class Default < Thor
41
41
 
42
42
  desc "clean", "clean VCR cassettes"
43
43
  def clean
44
- FileUtils.rm_rf("spec/fixtures/vcr_cassettes/*")
44
+ FileUtils.rm_rf("spec/fixtures/vcr_cassettes")
45
45
  end
46
46
  end
47
47
  end
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.version = Berkshelf::VERSION
17
17
  s.required_ruby_version = ">= 1.9.1"
18
18
 
19
- s.add_runtime_dependency 'dep_selector'
19
+ s.add_runtime_dependency 'solve', '~> 0.2.1'
20
20
  s.add_runtime_dependency 'chef', '~> 10.12.0'
21
21
  s.add_runtime_dependency 'minitar'
22
22
  s.add_runtime_dependency 'thor', '~> 0.15.2'
@@ -71,8 +71,8 @@ Feature: install cookbooks from a Berksfile
71
71
  cookbook "artifact", git: "git://github.com/RiotGames/artifact-cookbook.git", ref: "0.9.8"
72
72
  """
73
73
  When I run the install command
74
- Then the cookbook store should have the cookbooks:
75
- | artifact | 0.9.8 |
74
+ Then the cookbook store should have the git cookbooks:
75
+ | artifact | 0.9.8 | c0a0b456a4716a81645bef1369f5fd1a4e62ce6d |
76
76
  And the output should contain:
77
77
  """
78
78
  Installing artifact (0.9.8) from git: 'git://github.com/RiotGames/artifact-cookbook.git' with branch: '0.9.8'
@@ -147,6 +147,29 @@ Feature: install cookbooks from a Berksfile
147
147
  """
148
148
  And the exit status should be 0
149
149
 
150
+ Scenario: running install with --shims when current project is a cookbook and the 'metadata' is specified
151
+ Given a cookbook named "sparkle_motion"
152
+ And the cookbook "sparkle_motion" has the file "Berksfile" with:
153
+ """
154
+ metadata
155
+ """
156
+ When I cd to "sparkle_motion"
157
+ And I run the install command with flags:
158
+ | --shims |
159
+ Then the following directories should exist:
160
+ | cookbooks |
161
+ | cookbooks/sparkle_motion |
162
+ And the output should contain:
163
+ """
164
+ Shims written to:
165
+ """
166
+ And the output should contain:
167
+ """
168
+ Using sparkle_motion (0.0.0) at path:
169
+ """
170
+ And the exit status should be 0
171
+
172
+
150
173
  Scenario: installing a Berksfile that has a Git location source with an invalid Git URI
151
174
  Given I write to "Berksfile" with:
152
175
  """
@@ -197,3 +220,80 @@ Feature: install cookbooks from a Berksfile
197
220
  Installing artifact (0.10.0) from git: 'git://github.com/RiotGames/artifact-cookbook.git' with branch: '0.10.0'
198
221
  """
199
222
  And the exit status should be 0
223
+
224
+ Scenario: with a cookbook definition containing an invalid option
225
+ Given I write to "Berksfile" with:
226
+ """
227
+ cookbook "artifact", whatisthis: "I don't even know", anotherwat: "isthat"
228
+ """
229
+ When I run the install command
230
+ Then the output should contain:
231
+ """
232
+ Invalid options for Cookbook Source: 'whatisthis', 'anotherwat'.
233
+ """
234
+ And the CLI should exit with the status code for error "InternalError"
235
+
236
+ Scenario: with a cookbook definition containing a chef_api source location
237
+ Given I write to "Berksfile" with:
238
+ """
239
+ cookbook "artifact", chef_api: :knife
240
+ """
241
+ And the Chef server has cookbooks:
242
+ | artifact | 0.10.0 |
243
+ When I run the install command
244
+ Then the output should contain:
245
+ """
246
+ Installing artifact (0.10.0) from chef_api:
247
+ """
248
+ And the cookbook store should have the cookbooks:
249
+ | artifact | 0.10.0 |
250
+ And the exit status should be 0
251
+
252
+ Scenario: with a chef_api source location specifying :knife when a Knife config is not found at the given path
253
+ Given I write to "Berksfile" with:
254
+ """
255
+ cookbook "artifact", chef_api: :knife
256
+ """
257
+ When I run the install command with flags:
258
+ | -c /tmp/nothere.lol |
259
+ Then the output should contain:
260
+ """
261
+ A Knife config is required when ':knife' is given for the value of a 'chef_api' location. Attempted to load configuration from: '/tmp/nothere.lol' but not found.
262
+ """
263
+ And the CLI should exit with the status code for error "KnifeConfigNotFound"
264
+
265
+ Scenario: with a chef_api source location specifying a Chef API URL but missing a node_name option
266
+ Given I write to "Berksfile" with:
267
+ """
268
+ cookbook "artifact", chef_api: "https://api.opscode.com/organizations/vialstudios", client_key: "/Users/reset/.chef/knife.rb"
269
+ """
270
+ When I run the install command
271
+ Then the output should contain:
272
+ """
273
+ Source 'artifact' is a 'chef_api' location with a URL for it's value but is missing options: 'node_name'.
274
+ """
275
+ And the CLI should exit with the status code for error "InvalidChefAPILocation"
276
+
277
+ Scenario: with a chef_api source location specifying a Chef API URL but missing a client_key option
278
+ Given I write to "Berksfile" with:
279
+ """
280
+ cookbook "artifact", chef_api: "https://api.opscode.com/organizations/vialstudios", node_name: "reset"
281
+ """
282
+ When I run the install command
283
+ Then the output should contain:
284
+ """
285
+ Source 'artifact' is a 'chef_api' location with a URL for it's value but is missing options: 'client_key'.
286
+ """
287
+ And the CLI should exit with the status code for error "InvalidChefAPILocation"
288
+
289
+ Scenario: with a chef_api source location specifying a Chef API URL but missing a client_key option
290
+ Given I write to "Berksfile" with:
291
+ """
292
+ cookbook "artifact", chef_api: "https://api.opscode.com/organizations/vialstudios"
293
+ """
294
+ When I run the install command
295
+ Then the output should contain:
296
+ """
297
+ Source 'artifact' is a 'chef_api' location with a URL for it's value but is missing options: 'node_name', 'client_key'.
298
+ """
299
+ And the CLI should exit with the status code for error "InvalidChefAPILocation"
@@ -17,6 +17,6 @@ Feature: Berksfile.lock
17
17
  cookbook 'ntp', :locked_version => '1.1.8'
18
18
  cookbook 'mysql', :git => 'https://github.com/opscode-cookbooks/mysql.git', :ref => '190c0c2267785b7b9b303369b8a64ed04364d5f9'
19
19
  cookbook 'openssl', :locked_version => '1.0.0'
20
- cookbook 'windows', :locked_version => '1.3.0'
20
+ cookbook 'windows', :locked_version => '1.3.2'
21
21
  cookbook 'chef_handler', :locked_version => '1.0.6'
22
22
  """
@@ -6,6 +6,14 @@ Given /^the Chef server does not have the cookbooks:$/ do |cookbooks|
6
6
  end
7
7
  end
8
8
 
9
+ Given /^the Chef server has cookbooks:$/ do |cookbooks|
10
+ cookbooks.raw.each do |name, version|
11
+ purge_cookbook(name, version)
12
+ cb_path = generate_cookbook(tmp_path, name, version)
13
+ upload_cookbook(cb_path)
14
+ end
15
+ end
16
+
9
17
  Then /^the Chef server should have the cookbooks:$/ do |cookbooks|
10
18
  cookbooks.raw.each do |name, version|
11
19
  server_has_cookbook?(name, version).should be_true
@@ -43,7 +43,7 @@ When /^I run the install command$/ do
43
43
  end
44
44
 
45
45
  When /^I run the install command with flags:$/ do |flags|
46
- run_simple(unescape("berks install #{flags.raw.join(" ")}"), true)
46
+ run_simple(unescape("berks install #{flags.raw.join(" ")}"), false)
47
47
  end
48
48
 
49
49
  When /^I run the update command$/ do
@@ -45,6 +45,18 @@ Then /^the cookbook store should have the cookbooks:$/ do |cookbooks|
45
45
  end
46
46
  end
47
47
 
48
+ Then /^the cookbook store should have the git cookbooks:$/ do |cookbooks|
49
+ cookbooks.raw.each do |name, version, sha1|
50
+ cookbook_store.should have_structure {
51
+ directory "#{name}-#{sha1}" do
52
+ file "metadata.rb" do
53
+ contains version
54
+ end
55
+ end
56
+ }
57
+ end
58
+ end
59
+
48
60
  Then /^the cookbook store should not have the cookbooks:$/ do |cookbooks|
49
61
  cookbooks.raw.each do |name, version|
50
62
  cookbook_store.should_not have_structure {
@@ -12,12 +12,6 @@ Spork.prefork do
12
12
 
13
13
  ENV["BERKSHELF_PATH"] = File.join(APP_ROOT, "tmp", "berkshelf")
14
14
 
15
- begin
16
- CONFIG = YAML.load(File.read(File.join(APP_ROOT, "features", "config.yml")))
17
- rescue Errno::ENOENT
18
- raise "Please create a config file at features/config.yml from the sample found at features/config.sample.yml"
19
- end
20
-
21
15
  Dir[File.join(APP_ROOT, "spec/support/**/*.rb")].each {|f| require f}
22
16
 
23
17
  Around do |scenario, block|
@@ -27,15 +21,13 @@ Spork.prefork do
27
21
  end
28
22
 
29
23
  Before do
30
- Chef::Config[:chef_server_url] = CONFIG['chef_server_url']
31
- Chef::Config[:client_key] = CONFIG['client_key']
32
- Chef::Config[:node_name] = CONFIG['node_name']
33
24
  clean_cookbook_store
34
25
  @aruba_io_wait_seconds = 5
26
+ @aruba_timeout_seconds = 8
35
27
  end
36
28
 
37
29
  Before('@slow_process') do
38
- @aruba_timeout_seconds = 15
30
+ @aruba_timeout_seconds = 60
39
31
  @aruba_io_wait_seconds = 10
40
32
  end
41
33
 
@@ -52,11 +44,17 @@ Spork.prefork do
52
44
  Pathname.new(APP_ROOT)
53
45
  end
54
46
 
47
+ def tmp_path
48
+ app_root_path.join('spec/tmp')
49
+ end
50
+
55
51
  def fixtures_path
56
52
  app_root_path.join('spec/fixtures')
57
53
  end
58
54
  end
59
55
 
60
56
  Spork.each_run do
57
+ Berkshelf::RSpec::Knife.load_knife_config(File.join(APP_ROOT, 'spec/knife.rb'))
58
+
61
59
  require 'berkshelf'
62
60
  end
@@ -18,6 +18,6 @@ Feature: update
18
18
  """
19
19
  cookbook 'mysql', :locked_version => '1.2.6'
20
20
  cookbook 'openssl', :locked_version => '1.0.0'
21
- cookbook 'windows', :locked_version => '1.3.0'
21
+ cookbook 'windows', :locked_version => '1.3.2'
22
22
  cookbook 'chef_handler', :locked_version => '1.0.6'
23
23
  """
@@ -1,11 +1,12 @@
1
1
  require 'pathname'
2
- require 'dep_selector'
3
2
  require 'zlib'
4
3
  require 'archive/tar/minitar'
4
+ require 'solve'
5
5
  require 'chef/knife'
6
6
  require 'chef/rest'
7
7
  require 'chef/platform'
8
8
  require 'chef/cookbook/metadata'
9
+ require 'chef/cookbook_version'
9
10
 
10
11
  require 'berkshelf/version'
11
12
  require 'berkshelf/core_ext'
@@ -14,6 +15,7 @@ require 'berkshelf/errors'
14
15
  Chef::Config[:cache_options][:path] = Dir.mktmpdir
15
16
 
16
17
  module Berkshelf
18
+ DEFAULT_CONFIG = File.expand_path(ENV["CHEF_CONFIG"] || "~/.chef/knife.rb")
17
19
  DEFAULT_STORE_PATH = File.expand_path("~/.berkshelf").freeze
18
20
  DEFAULT_FILENAME = 'Berksfile'.freeze
19
21
 
@@ -37,6 +39,8 @@ module Berkshelf
37
39
  attr_accessor :cookbook_store
38
40
  attr_accessor :downloader
39
41
 
42
+ attr_writer :config_path
43
+
40
44
  def root
41
45
  @root ||= Pathname.new(File.expand_path('../', File.dirname(__FILE__)))
42
46
  end
@@ -62,6 +66,20 @@ module Berkshelf
62
66
  @downloader ||= Downloader.new(cookbook_store)
63
67
  end
64
68
 
69
+ def config_path
70
+ @config_path ||= DEFAULT_CONFIG
71
+ end
72
+
73
+ # Load the config found at the given path as the Chef::Config. If no path is specified
74
+ # the value of Berkshelf.chef_config will be used.
75
+ #
76
+ # @param [String] path
77
+ def load_config(path = config_path)
78
+ Chef::Config.from_file(File.expand_path(path))
79
+ rescue Errno::ENOENT
80
+ raise KnifeConfigNotFound, "Attempted to load configuration from: '#{path}' but not found."
81
+ end
82
+
65
83
  # Ascend the directory structure from the given path to find a
66
84
  # metadata.rb file of a Chef Cookbook. If no metadata.rb file
67
85
  # was found, nil is returned.
@@ -185,21 +185,33 @@ module Berkshelf
185
185
  # @param [Pathname, String] path
186
186
  # @param [Array<Berkshelf::CachedCookbook>] cached_cookbooks
187
187
  def write_shims(path, cached_cookbooks)
188
+ actual_path = nil
189
+
190
+ if descendant_directory?(path, Dir.pwd)
191
+ actual_path = path
192
+ FileUtils.rm_rf(actual_path)
193
+ path = Dir.mktmpdir("berkshelf-")
194
+ end
195
+
188
196
  FileUtils.mkdir_p(path)
189
197
  cached_cookbooks.each do |cached_cookbook|
190
198
  destination = File.expand_path(File.join(path, cached_cookbook.cookbook_name))
191
199
  FileUtils.rm_rf(destination)
192
- begin
193
- FileUtils.ln_r(cached_cookbook.path, destination, force: true)
194
- rescue ArgumentError
195
- Berkshelf.ui.warn "Skipping shim for #{cached_cookbook}."
196
- Berkshelf.ui.warn "Cannot write a shim for a path location source into a subdirectory of itself."
197
- end
200
+ FileUtils.ln_r(cached_cookbook.path, destination, force: true)
201
+ end
202
+
203
+ if actual_path
204
+ FileUtils.mv(path, actual_path)
198
205
  end
199
206
  end
200
207
 
201
208
  private
202
209
 
210
+ def descendant_directory?(candidate, parent)
211
+ hack = FileUtils::Entry_.new('/tmp')
212
+ hack.send(:descendant_diretory?, candidate, parent)
213
+ end
214
+
203
215
  def lockfile_present?
204
216
  File.exist?(Berkshelf::Lockfile::DEFAULT_FILENAME)
205
217
  end
@@ -8,6 +8,7 @@ module Berkshelf
8
8
  super
9
9
  # JW TODO: Replace Chef::Knife::UI with our own UI class
10
10
  ::Berkshelf.ui = Chef::Knife::UI.new(STDOUT, STDERR, STDIN, {})
11
+ ::Berkshelf.config_path = @options[:config]
11
12
  @options = options.dup # unfreeze frozen options Hash from Thor
12
13
  rescue BerkshelfError => e
13
14
  Berkshelf.ui.fatal e
@@ -23,7 +24,7 @@ module Berkshelf
23
24
 
24
25
  class_option :config,
25
26
  type: :string,
26
- default: File.expand_path(ENV["CHEF_CONFIG"] || "~/.chef/knife.rb"),
27
+ default: Berkshelf::DEFAULT_CONFIG,
27
28
  desc: "Path to Knife or Chef configuration to use.",
28
29
  aliases: "-c",
29
30
  banner: "PATH"
@@ -95,7 +96,7 @@ module Berkshelf
95
96
  desc: "Upload all cookbooks even if a frozen one exists on the target Chef Server"
96
97
  desc "upload", "Upload the Cookbooks specified by a Berksfile or a Berksfile.lock to a Chef Server."
97
98
  def upload
98
- load_config
99
+ Berkshelf.load_config
99
100
  berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile])
100
101
  berksfile.upload(Chef::Config[:chef_server_url], options)
101
102
  rescue BerkshelfError => e
@@ -135,11 +136,5 @@ module Berkshelf
135
136
  def license
136
137
  File.read(Berkshelf.root.join('LICENSE'))
137
138
  end
138
-
139
- def load_config
140
- Chef::Config.from_file(File.expand_path(options[:config]))
141
- rescue Errno::ENOENT
142
- raise KnifeConfigNotFound, "Unable to find a Knife config at #{options[:config]}. Specify a different path with --config."
143
- end
144
139
  end
145
140
  end
@@ -1,14 +1,65 @@
1
1
  module Berkshelf
2
2
  # @author Jamie Winsor <jamie@vialstudios.com>
3
3
  class CookbookSource
4
- extend Forwardable
4
+ class << self
5
+ @@valid_options = [:group, :locked_version]
6
+ @@location_keys = Hash.new
7
+
8
+ # Returns an array of valid options to pass to the initializer
9
+ #
10
+ # @return [Array<Symbol>]
11
+ def valid_options
12
+ @@valid_options
13
+ end
14
+
15
+ # Returns an array of the registered source location keys. Every source
16
+ # location is identified by a key (symbol) to differentiate which class
17
+ # to instantiate for the location of a CookbookSource at initialization.
18
+ #
19
+ # @return [Array<Symbol>]
20
+ def location_keys
21
+ @@location_keys
22
+ end
23
+
24
+ # Add a option to the list of valid options
25
+ # @see #valid_options
26
+ #
27
+ # @param [Symbol] option
28
+ #
29
+ # @return [Array<Symbol>]
30
+ def add_valid_option(option)
31
+ @@valid_options.push(option) unless @@valid_options.include?(option)
32
+ @@valid_options
33
+ end
5
34
 
6
- autoload :Location, 'berkshelf/cookbook_source/location'
7
- autoload :SiteLocation, 'berkshelf/cookbook_source/site_location'
8
- autoload :GitLocation, 'berkshelf/cookbook_source/git_location'
9
- autoload :PathLocation, 'berkshelf/cookbook_source/path_location'
35
+ # Register a location key with the CookbookSource class
36
+ # @see #location_keys
37
+ #
38
+ # @param [Symbol] location
39
+ #
40
+ # @raise [ArgumentError] if the location key has already been defined
41
+ #
42
+ # @return [Array<Symbol>]
43
+ def add_location_key(location, klass)
44
+ unless @@location_keys.has_key?(location)
45
+ add_valid_option(location)
46
+ @@location_keys[location] = klass
47
+ end
48
+
49
+ @@location_keys
50
+ end
51
+ end
52
+
53
+ extend Forwardable
10
54
 
11
- LOCATION_KEYS = [:git, :path, :site]
55
+ # JW TODO: Move locations out of CookbookSource namespace.
56
+ # Move all locations into berkshelf/locations/*
57
+ # Autorequire all items in berkshelf/locations/
58
+ require 'berkshelf/cookbook_source/location'
59
+ require 'berkshelf/cookbook_source/site_location'
60
+ require 'berkshelf/cookbook_source/git_location'
61
+ require 'berkshelf/cookbook_source/path_location'
62
+ require 'berkshelf/cookbook_source/chef_api_location'
12
63
 
13
64
  attr_reader :name
14
65
  alias_method :to_s, :name
@@ -24,36 +75,59 @@ module Berkshelf
24
75
  # @param [#to_s] name
25
76
  # @param [#to_s] version_constraint
26
77
  # @param [Hash] options
78
+ #
79
+ # @option options [String] :git
80
+ # the Git URL to clone
81
+ # @option options [String] :site
82
+ # a URL pointing to a community API endpoint
83
+ # @option options [String] :path
84
+ # a filepath to the cookbook on your local disk
85
+ # @option options [Symbol, Array] :group
86
+ # the group or groups that the cookbook belongs to
87
+ # @option options [String] :ref
88
+ # the commit hash or an alias to a commit hash to clone
89
+ # @option options [String] :branch
90
+ # same as ref
91
+ # @option options [String] :tag
92
+ # same as tag
93
+ # @option options [String] :locked_version
27
94
  # @overload initialize(name, options = {})
28
95
  # @param [#to_s] name
29
96
  # @param [Hash] options
97
+ #
98
+ # @option options [String] :git
99
+ # the Git URL to clone
100
+ # @option options [String] :site
101
+ # a URL pointing to a community API endpoint
102
+ # @option options [String] :path
103
+ # a filepath to the cookbook on your local disk
104
+ # @option options [Symbol, Array] :group
105
+ # the group or groups that the cookbook belongs to
106
+ # @option options [String] :ref
107
+ # the commit hash or an alias to a commit hash to clone
108
+ # @option options [String] :branch
109
+ # same as ref
110
+ # @option options [String] :tag
111
+ # same as tag
112
+ # @option options [String] :locked_version
30
113
  def initialize(*args)
31
114
  options = args.last.is_a?(Hash) ? args.pop : {}
32
115
  name, constraint = args
33
116
 
34
117
  @name = name
35
- @version_constraint = DepSelector::VersionConstraint.new(constraint || ">= 0.0.0")
118
+ @version_constraint = Solve::Constraint.new(constraint || ">= 0.0.0")
36
119
  @groups = []
37
120
  @cached_cookbook = nil
38
121
 
39
- if (options.keys & LOCATION_KEYS).length > 1
40
- raise ArgumentError, "Only one location key (#{LOCATION_KEYS.join(', ')}) may be specified"
41
- end
122
+ validate_options(options)
42
123
 
43
- @location = case
44
- when options[:git]
45
- GitLocation.new(name, version_constraint, options)
46
- when options[:path]
47
- loc = PathLocation.new(name, version_constraint, options)
48
- @cached_cookbook = CachedCookbook.from_path(loc.path)
49
- loc
50
- when options[:site]
51
- SiteLocation.new(name, version_constraint, options)
52
- else
53
- SiteLocation.new(name, version_constraint, options)
54
- end
124
+ @location = Location.init(name, version_constraint, options)
55
125
 
56
- @locked_version = DepSelector::Version.new(options[:locked_version]) if options[:locked_version]
126
+ if @location.is_a?(PathLocation)
127
+ @cached_cookbook = CachedCookbook.from_path(@location.path)
128
+ end
129
+
130
+ @locked_version = Solve::Version.new(options[:locked_version]) if options[:locked_version]
57
131
 
58
132
  add_group(options[:group]) if options[:group]
59
133
  add_group(:default) if groups.empty?
@@ -98,5 +172,16 @@ module Berkshelf
98
172
  def set_local_path(path)
99
173
  @local_path = path
100
174
  end
175
+
176
+ def validate_options(options)
177
+ invalid_options = options.keys - self.class.valid_options
178
+
179
+ unless invalid_options.empty?
180
+ invalid_options.collect! { |opt| "'#{opt}'" }
181
+ raise InternalError, "Invalid options for Cookbook Source: #{invalid_options.join(', ')}."
182
+ end
183
+
184
+ true
185
+ end
101
186
  end
102
187
  end