berkshelf 0.4.0.rc4 → 0.4.0
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.
- data/.gitignore +2 -0
- data/Guardfile +6 -3
- data/features/default_locations.feature +122 -0
- data/features/install.feature +20 -4
- data/features/lockfile.feature +1 -6
- data/features/update.feature +2 -3
- data/generator_files/Berksfile.erb +2 -0
- data/generator_files/gitignore.erb +6 -0
- data/lib/berkshelf.rb +6 -10
- data/lib/berkshelf/berksfile.rb +203 -14
- data/lib/berkshelf/cached_cookbook.rb +5 -1
- data/lib/berkshelf/cli.rb +4 -0
- data/lib/berkshelf/cookbook_source.rb +49 -91
- data/lib/berkshelf/cookbook_store.rb +2 -0
- data/lib/berkshelf/downloader.rb +71 -51
- data/lib/berkshelf/errors.rb +7 -3
- data/lib/berkshelf/formatters.rb +6 -6
- data/lib/berkshelf/location.rb +171 -0
- data/lib/berkshelf/locations/chef_api_location.rb +252 -0
- data/lib/berkshelf/locations/git_location.rb +76 -0
- data/lib/berkshelf/locations/path_location.rb +38 -0
- data/lib/berkshelf/locations/site_location.rb +150 -0
- data/lib/berkshelf/lockfile.rb +2 -2
- data/lib/berkshelf/resolver.rb +12 -15
- data/lib/berkshelf/uploader.rb +2 -9
- data/lib/berkshelf/version.rb +1 -1
- data/spec/fixtures/lockfile_spec/without_lock/.gitkeep +0 -0
- data/spec/support/chef_api.rb +7 -1
- data/spec/unit/berkshelf/berksfile_spec.rb +157 -12
- data/spec/unit/berkshelf/cached_cookbook_spec.rb +19 -0
- data/spec/unit/berkshelf/cookbook_generator_spec.rb +1 -0
- data/spec/unit/berkshelf/cookbook_source_spec.rb +25 -35
- data/spec/unit/berkshelf/cookbook_store_spec.rb +3 -3
- data/spec/unit/berkshelf/downloader_spec.rb +171 -43
- data/spec/unit/berkshelf/formatters_spec.rb +13 -16
- data/spec/unit/berkshelf/{cookbook_source/location_spec.rb → location_spec.rb} +10 -10
- data/spec/unit/berkshelf/{cookbook_source → locations}/chef_api_location_spec.rb +4 -4
- data/spec/unit/berkshelf/{cookbook_source → locations}/git_location_spec.rb +8 -8
- data/spec/unit/berkshelf/{cookbook_source → locations}/path_location_spec.rb +5 -5
- data/spec/unit/berkshelf/{cookbook_source → locations}/site_location_spec.rb +17 -3
- data/spec/unit/berkshelf/lockfile_spec.rb +26 -17
- data/spec/unit/berkshelf/resolver_spec.rb +6 -5
- data/spec/unit/berkshelf/uploader_spec.rb +6 -4
- metadata +27 -31
- data/lib/berkshelf/cookbook_source/chef_api_location.rb +0 -256
- data/lib/berkshelf/cookbook_source/git_location.rb +0 -78
- data/lib/berkshelf/cookbook_source/location.rb +0 -167
- data/lib/berkshelf/cookbook_source/path_location.rb +0 -40
- data/lib/berkshelf/cookbook_source/site_location.rb +0 -149
- data/lib/berkshelf/dsl.rb +0 -45
- data/lib/berkshelf/tx_result.rb +0 -12
- data/lib/berkshelf/tx_result_set.rb +0 -37
- data/spec/fixtures/lockfile_spec/without_lock/Berksfile.lock +0 -5
- data/spec/unit/berkshelf/dsl_spec.rb +0 -42
- data/spec/unit/berkshelf/tx_result_set_spec.rb +0 -77
- data/spec/unit/berkshelf/tx_result_spec.rb +0 -21
@@ -94,7 +94,6 @@ module Berkshelf
|
|
94
94
|
attr_reader :manifest
|
95
95
|
|
96
96
|
def_delegator :@metadata, :version
|
97
|
-
def_delegator :@metadata, :dependencies
|
98
97
|
|
99
98
|
def initialize(name, path, metadata)
|
100
99
|
@cookbook_name = name
|
@@ -125,6 +124,11 @@ module Berkshelf
|
|
125
124
|
"#{cookbook_name}-#{version}"
|
126
125
|
end
|
127
126
|
|
127
|
+
# @return [Hash]
|
128
|
+
def dependencies
|
129
|
+
metadata.recommendations.merge(metadata.dependencies)
|
130
|
+
end
|
131
|
+
|
128
132
|
# @return [Hash]
|
129
133
|
# an hash containing the checksums and expanded file paths of all of the
|
130
134
|
# files found in the instance of CachedCookbook
|
data/lib/berkshelf/cli.rb
CHANGED
@@ -59,6 +59,10 @@ module Berkshelf
|
|
59
59
|
banner: "PATH"
|
60
60
|
desc "install", "Install the Cookbooks specified by a Berksfile or a Berksfile.lock."
|
61
61
|
def install
|
62
|
+
unless options[:shims].nil?
|
63
|
+
options[:shims] = File.expand_path(options[:shims])
|
64
|
+
end
|
65
|
+
|
62
66
|
berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile])
|
63
67
|
berksfile.install(options)
|
64
68
|
end
|
@@ -2,7 +2,7 @@ module Berkshelf
|
|
2
2
|
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
3
|
class CookbookSource
|
4
4
|
class << self
|
5
|
-
@@valid_options = [:group, :locked_version]
|
5
|
+
@@valid_options = [:constraint, :locations, :group, :locked_version]
|
6
6
|
@@location_keys = Hash.new
|
7
7
|
|
8
8
|
# Returns an array of valid options to pass to the initializer
|
@@ -48,83 +48,62 @@ module Berkshelf
|
|
48
48
|
|
49
49
|
@@location_keys
|
50
50
|
end
|
51
|
+
|
52
|
+
def validate_options(options)
|
53
|
+
invalid_options = (options.keys - valid_options)
|
54
|
+
|
55
|
+
unless invalid_options.empty?
|
56
|
+
invalid_options.collect! { |opt| "'#{opt}'" }
|
57
|
+
raise InternalError, "Invalid options for Cookbook Source: #{invalid_options.join(', ')}."
|
58
|
+
end
|
59
|
+
|
60
|
+
true
|
61
|
+
end
|
51
62
|
end
|
52
63
|
|
53
64
|
extend Forwardable
|
54
65
|
|
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'
|
63
|
-
|
64
66
|
attr_reader :name
|
65
|
-
alias_method :to_s, :name
|
66
|
-
|
67
67
|
attr_reader :version_constraint
|
68
68
|
attr_reader :groups
|
69
69
|
attr_reader :location
|
70
70
|
attr_accessor :cached_cookbook
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
# @overload initialize(name, version_constraint, options = {})
|
75
|
-
# @param [#to_s] name
|
76
|
-
# @param [#to_s] version_constraint
|
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
|
94
|
-
# @overload initialize(name, options = {})
|
95
|
-
# @param [#to_s] name
|
96
|
-
# @param [Hash] options
|
72
|
+
# @param [String] name
|
73
|
+
# @param [Hash] options
|
97
74
|
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
112
|
-
#
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
75
|
+
# @option options [String, Solve::Constraint] constraint
|
76
|
+
# version constraint to resolve for this source
|
77
|
+
# @option options [String] :git
|
78
|
+
# the Git URL to clone
|
79
|
+
# @option options [String] :site
|
80
|
+
# a URL pointing to a community API endpoint
|
81
|
+
# @option options [String] :path
|
82
|
+
# a filepath to the cookbook on your local disk
|
83
|
+
# @option options [Symbol, Array] :group
|
84
|
+
# the group or groups that the cookbook belongs to
|
85
|
+
# @option options [String] :ref
|
86
|
+
# the commit hash or an alias to a commit hash to clone
|
87
|
+
# @option options [String] :branch
|
88
|
+
# same as ref
|
89
|
+
# @option options [String] :tag
|
90
|
+
# same as tag
|
91
|
+
# @option options [String] :locked_version
|
92
|
+
def initialize(name, options = {})
|
117
93
|
@name = name
|
118
|
-
@version_constraint = Solve::Constraint.new(constraint || ">= 0.0.0")
|
94
|
+
@version_constraint = Solve::Constraint.new(options[:constraint] || ">= 0.0.0")
|
119
95
|
@groups = []
|
120
96
|
@cached_cookbook = nil
|
97
|
+
@location = nil
|
121
98
|
|
122
|
-
validate_options(options)
|
99
|
+
self.class.validate_options(options)
|
123
100
|
|
124
|
-
|
101
|
+
unless (options.keys & self.class.location_keys.keys).empty?
|
102
|
+
@location = Location.init(name, version_constraint, options)
|
103
|
+
end
|
125
104
|
|
126
105
|
if @location.is_a?(PathLocation)
|
127
|
-
@cached_cookbook = CachedCookbook.from_path(
|
106
|
+
@cached_cookbook = CachedCookbook.from_path(location.path)
|
128
107
|
end
|
129
108
|
|
130
109
|
@locked_version = Solve::Version.new(options[:locked_version]) if options[:locked_version]
|
@@ -141,22 +120,12 @@ module Berkshelf
|
|
141
120
|
end
|
142
121
|
end
|
143
122
|
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
# @return [Array]
|
148
|
-
# An array containing the status at index 0 and local path or error message in index 1
|
123
|
+
# Returns true if the cookbook source has already been downloaded. A cookbook
|
124
|
+
# source is downloaded when a cached cookbooked is present.
|
149
125
|
#
|
150
|
-
#
|
151
|
-
|
152
|
-
|
153
|
-
def download(destination)
|
154
|
-
self.cached_cookbook = location.download(destination)
|
155
|
-
|
156
|
-
[ :ok, self.cached_cookbook ]
|
157
|
-
rescue CookbookNotFound => e
|
158
|
-
self.cached_cookbook = nil
|
159
|
-
[ :error, e.message ]
|
126
|
+
# @return [Boolean]
|
127
|
+
def downloaded?
|
128
|
+
!self.cached_cookbook.nil?
|
160
129
|
end
|
161
130
|
|
162
131
|
def has_group?(group)
|
@@ -167,21 +136,10 @@ module Berkshelf
|
|
167
136
|
@locked_version || cached_cookbook.version
|
168
137
|
end
|
169
138
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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
|
139
|
+
def to_s
|
140
|
+
msg = "#{self.name} (#{self.version_constraint}) groups: #{self.groups}"
|
141
|
+
msg << " location: #{self.location}" if self.location
|
142
|
+
msg
|
143
|
+
end
|
186
144
|
end
|
187
145
|
end
|
data/lib/berkshelf/downloader.rb
CHANGED
@@ -3,80 +3,100 @@ module Berkshelf
|
|
3
3
|
class Downloader
|
4
4
|
extend Forwardable
|
5
5
|
|
6
|
+
DEFAULT_LOCATIONS = [
|
7
|
+
{
|
8
|
+
type: :site,
|
9
|
+
value: Location::OPSCODE_COMMUNITY_API,
|
10
|
+
options: Hash.new
|
11
|
+
}
|
12
|
+
]
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
# a filepath to download cookbook sources to
|
6
16
|
attr_reader :cookbook_store
|
7
|
-
attr_reader :queue
|
8
17
|
|
9
18
|
def_delegators :@cookbook_store, :storage_path
|
10
19
|
|
11
|
-
|
20
|
+
# @option options [Array<Hash>] locations
|
21
|
+
def initialize(cookbook_store, options = {})
|
12
22
|
@cookbook_store = cookbook_store
|
13
|
-
@
|
23
|
+
@locations = options.fetch(:locations, Array.new)
|
14
24
|
end
|
15
25
|
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
def enqueue(source)
|
23
|
-
unless validate_source(source)
|
24
|
-
raise ArgumentError, "Invalid CookbookSource: can only enqueue valid instances of CookbookSource."
|
25
|
-
end
|
26
|
-
|
27
|
-
@queue << source
|
26
|
+
# @return [Array<Hash>]
|
27
|
+
# an Array of Hashes representing each default location that can be used to attempt
|
28
|
+
# to download cookbook sources which do not have an explicit location. An array of default locations will
|
29
|
+
# be used if no locations are explicitly added by the {#add_location} function.
|
30
|
+
def locations
|
31
|
+
@locations.empty? ? DEFAULT_LOCATIONS : @locations
|
28
32
|
end
|
29
33
|
|
30
|
-
#
|
34
|
+
# Create a location hash and add it to the end of the array of locations.
|
31
35
|
#
|
32
|
-
#
|
36
|
+
# subject.add_location(:chef_api, "http://chef:8080", node_name: "reset", client_key: "/Users/reset/.chef/reset.pem") =>
|
37
|
+
# [ { type: :chef_api, value: "http://chef:8080/", node_name: "reset", client_key: "/Users/reset/.chef/reset.pem" } ]
|
33
38
|
#
|
34
|
-
# @
|
35
|
-
#
|
36
|
-
|
37
|
-
@queue.delete(source)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Download each CookbookSource in the queue. Upon successful download
|
41
|
-
# of a CookbookSource it is removed from the queue. If a CookbookSource
|
42
|
-
# fails to download it remains in the queue.
|
39
|
+
# @param [Symbol] type
|
40
|
+
# @param [String, Symbol] value
|
41
|
+
# @param [Hash] options
|
43
42
|
#
|
44
|
-
# @return [
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
queue.each do |source|
|
49
|
-
results.add_result download(source)
|
43
|
+
# @return [Hash]
|
44
|
+
def add_location(type, value, options = {})
|
45
|
+
if has_location?(type, value)
|
46
|
+
raise DuplicateLocationDefined, "A default '#{type}' location with the value '#{value}' is already defined"
|
50
47
|
end
|
51
|
-
|
52
|
-
results.success.each { |result| dequeue(result.source) }
|
53
48
|
|
54
|
-
|
49
|
+
@locations.push(type: type, value: value, options: options)
|
55
50
|
end
|
56
51
|
|
57
|
-
#
|
58
|
-
#
|
59
|
-
# @param [CookbookSource] source
|
60
|
-
# the source to download
|
52
|
+
# Checks the list of default locations if a location of the given type and value has already
|
53
|
+
# been added and returns true or false.
|
61
54
|
#
|
62
|
-
# @return [
|
63
|
-
def
|
64
|
-
|
65
|
-
TXResult.new(status, message, source)
|
55
|
+
# @return [Boolean]
|
56
|
+
def has_location?(type, value)
|
57
|
+
!@locations.select { |loc| loc[:type] == type && loc[:value] == value }.empty?
|
66
58
|
end
|
67
59
|
|
68
|
-
# Downloads the given CookbookSource.
|
69
|
-
#
|
60
|
+
# Downloads the given CookbookSource. If the given source does not contain a value for {CookbookSource#location}
|
61
|
+
# the default locations of this downloader will be used to attempt to retrieve the source.
|
70
62
|
#
|
71
63
|
# @param [CookbookSource] source
|
72
64
|
# the source to download
|
73
65
|
#
|
74
|
-
# @return [
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
66
|
+
# @return [Array]
|
67
|
+
# an array containing the downloaded CachedCookbook and the Location used to download the cookbook
|
68
|
+
def download(source)
|
69
|
+
cached_cookbook, location = if source.location
|
70
|
+
[ source.location.download(storage_path), source.location ]
|
71
|
+
else
|
72
|
+
cached_cookbook = nil
|
73
|
+
location = nil
|
74
|
+
|
75
|
+
locations.each do |loc|
|
76
|
+
location = Location.init(
|
77
|
+
source.name,
|
78
|
+
source.version_constraint,
|
79
|
+
loc[:options].merge(loc[:type] => loc[:value])
|
80
|
+
)
|
81
|
+
begin
|
82
|
+
cached_cookbook = location.download(storage_path)
|
83
|
+
break
|
84
|
+
rescue
|
85
|
+
cached_cookbook, location = nil
|
86
|
+
next
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
if cached_cookbook.nil?
|
91
|
+
raise CookbookNotFound, "Cookbook '#{source.name}' not found in any of the default locations"
|
92
|
+
end
|
93
|
+
|
94
|
+
[ cached_cookbook, location ]
|
95
|
+
end
|
96
|
+
|
97
|
+
source.cached_cookbook = cached_cookbook
|
98
|
+
|
99
|
+
[ cached_cookbook, location ]
|
80
100
|
end
|
81
101
|
|
82
102
|
private
|
data/lib/berkshelf/errors.rb
CHANGED
@@ -12,11 +12,15 @@ module Berkshelf
|
|
12
12
|
end
|
13
13
|
|
14
14
|
class InternalError < BerkshelfError; status_code(99); end
|
15
|
-
class
|
16
|
-
|
15
|
+
class AbstractFunction < InternalError
|
16
|
+
def to_s
|
17
|
+
"Function must be implemented on includer"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
17
21
|
class BerksfileNotFound < BerkshelfError; status_code(100); end
|
18
22
|
class NoVersionForConstraints < BerkshelfError; status_code(101); end
|
19
|
-
class
|
23
|
+
class DuplicateLocationDefined < BerkshelfError; status_code(102); end
|
20
24
|
class CookbookNotFound < BerkshelfError; status_code(103); end
|
21
25
|
class GitError < BerkshelfError
|
22
26
|
status_code(104)
|
data/lib/berkshelf/formatters.rb
CHANGED
@@ -10,27 +10,27 @@ module Berkshelf
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def install(cookbook, version, location)
|
13
|
-
raise
|
13
|
+
raise AbstractFunction, "#install must be implemented on #{self.class}"
|
14
14
|
end
|
15
15
|
|
16
16
|
def use(cookbook, version, path = nil)
|
17
|
-
raise
|
17
|
+
raise AbstractFunction, "#install must be implemented on #{self.class}"
|
18
18
|
end
|
19
19
|
|
20
20
|
def upload(cookbook, version, chef_server_url)
|
21
|
-
raise
|
21
|
+
raise AbstractFunction, "#upload must be implemented on #{self.class}"
|
22
22
|
end
|
23
23
|
|
24
24
|
def shims_written(directory)
|
25
|
-
raise
|
25
|
+
raise AbstractFunction, "#shims_written must be implemented on #{self.class}"
|
26
26
|
end
|
27
27
|
|
28
28
|
def msg(message)
|
29
|
-
raise
|
29
|
+
raise AbstractFunction, "#msg must be implemented on #{self.class}"
|
30
30
|
end
|
31
31
|
|
32
32
|
def error(message)
|
33
|
-
raise
|
33
|
+
raise AbstractFunction, "#error must be implemented on #{self.class}"
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
|
+
module Location
|
4
|
+
OPSCODE_COMMUNITY_API = 'http://cookbooks.opscode.com/api/v1/cookbooks'.freeze
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
# Register the location key for the including source location with CookbookSource
|
8
|
+
#
|
9
|
+
# @param [Symbol] key
|
10
|
+
def location_key(key)
|
11
|
+
CookbookSource.add_location_key(key, self)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Register a valid option or multiple options with the CookbookSource class
|
15
|
+
#
|
16
|
+
# @param [Symbol] opts
|
17
|
+
def valid_options(*opts)
|
18
|
+
Array(opts).each do |opt|
|
19
|
+
CookbookSource.add_valid_option(opt)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns an array where the first element is string representing the best version
|
24
|
+
# for the given constraint and the second element is the URI to where the corresponding
|
25
|
+
# version of the Cookbook can be downloaded from
|
26
|
+
#
|
27
|
+
# @example:
|
28
|
+
# constraint = Solve::Constraint.new("~> 0.101.2")
|
29
|
+
# versions = {
|
30
|
+
# "1.0.0" => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/1_0_0",
|
31
|
+
# "2.0.0" => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/2_0_0"
|
32
|
+
# }
|
33
|
+
#
|
34
|
+
# subject.solve_for_constraint(versions, constraint) =>
|
35
|
+
# [ "2.0.0", "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/2_0_0" ]
|
36
|
+
#
|
37
|
+
# @param [String, Solve::Constraint] constraint
|
38
|
+
# version constraint to solve for
|
39
|
+
#
|
40
|
+
# @param [Hash] versions
|
41
|
+
# a hash where the keys are a string representing a cookbook version and the values
|
42
|
+
# are the download URL for the cookbook version.
|
43
|
+
#
|
44
|
+
# @return [Array, nil]
|
45
|
+
def solve_for_constraint(constraint, versions)
|
46
|
+
graph = Solve::Graph.new
|
47
|
+
name = "none"
|
48
|
+
|
49
|
+
versions.each do |version, uri|
|
50
|
+
graph.artifacts(name, version)
|
51
|
+
end
|
52
|
+
|
53
|
+
graph.demands(name, constraint)
|
54
|
+
result = Solve.it(graph)
|
55
|
+
|
56
|
+
return nil if result.nil?
|
57
|
+
|
58
|
+
version = result[name]
|
59
|
+
|
60
|
+
[ version, versions[version] ]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class << self
|
65
|
+
def included(base)
|
66
|
+
base.send :extend, ClassMethods
|
67
|
+
end
|
68
|
+
|
69
|
+
# Creates a new instance of a Class implementing Location with the given name and
|
70
|
+
# constraint. Which Class to instantiated is determined by the values in the given
|
71
|
+
# options Hash. Source Locations have an associated location_key registered with
|
72
|
+
# CookbookSource. If your options Hash contains a key matching one of these location_keys
|
73
|
+
# then the Class who registered that location_key will be instantiated. If you do not
|
74
|
+
# provide an option with a matching location_key a SiteLocation class will be
|
75
|
+
# instantiated.
|
76
|
+
#
|
77
|
+
# @example
|
78
|
+
# Location.init("nginx", ">= 0.0.0", git: "git://github.com/RiotGames/artifact-cookbook.git") =>
|
79
|
+
# instantiates a GitLocation
|
80
|
+
#
|
81
|
+
# Location.init("nginx", ">= 0.0.0", path: "/Users/reset/code/nginx-cookbook") =>
|
82
|
+
# instantiates a PathLocation
|
83
|
+
#
|
84
|
+
# Location.init("nginx", ">= 0.0.0", site: "http://cookbooks.opscode.com/api/v1/cookbooks") =>
|
85
|
+
# instantiates a SiteLocation
|
86
|
+
#
|
87
|
+
# Location.init("nginx", ">= 0.0.0", chef_api: "https://api.opscode.com/organizations/vialstudios") =>
|
88
|
+
# instantiates a ChefAPILocation
|
89
|
+
#
|
90
|
+
# Location.init("nginx", ">= 0.0.0") =>
|
91
|
+
# instantiates a SiteLocation
|
92
|
+
#
|
93
|
+
# @param [String] name
|
94
|
+
# @param [String, Solve::Constraint] constraint
|
95
|
+
# @param [Hash] options
|
96
|
+
#
|
97
|
+
# @return [SiteLocation, PathLocation, GitLocation, ChefAPILocation]
|
98
|
+
def init(name, constraint, options = {})
|
99
|
+
klass = klass_from_options(options)
|
100
|
+
|
101
|
+
klass.new(name, constraint, options)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def klass_from_options(options)
|
107
|
+
location_keys = (options.keys & CookbookSource.location_keys.keys)
|
108
|
+
if location_keys.length > 1
|
109
|
+
location_keys.collect! { |opt| "'#{opt}'" }
|
110
|
+
raise InternalError, "Only one location key (#{CookbookSource.location_keys.keys.join(', ')}) may be specified. You gave #{location_keys.join(', ')}."
|
111
|
+
end
|
112
|
+
|
113
|
+
if location_keys.empty?
|
114
|
+
SiteLocation
|
115
|
+
else
|
116
|
+
CookbookSource.location_keys[location_keys.first]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
attr_reader :name
|
122
|
+
attr_reader :version_constraint
|
123
|
+
|
124
|
+
# @param [#to_s] name
|
125
|
+
# @param [Solve::Constraint] version_constraint
|
126
|
+
# @param [Hash] options
|
127
|
+
def initialize(name, version_constraint, options = {})
|
128
|
+
@name = name
|
129
|
+
@version_constraint = version_constraint
|
130
|
+
@downloaded_status = false
|
131
|
+
end
|
132
|
+
|
133
|
+
# @param [#to_s] destination
|
134
|
+
#
|
135
|
+
# @return [Berkshelf::CachedCookbook]
|
136
|
+
def download(destination)
|
137
|
+
raise AbstractFunction
|
138
|
+
end
|
139
|
+
|
140
|
+
# @return [Boolean]
|
141
|
+
def downloaded?
|
142
|
+
@downloaded_status
|
143
|
+
end
|
144
|
+
|
145
|
+
# Ensures that the given CachedCookbook satisfies the constraint
|
146
|
+
#
|
147
|
+
# @param [CachedCookbook] cached_cookbook
|
148
|
+
#
|
149
|
+
# @raise [ConstraintNotSatisfied] if the CachedCookbook does not satisfy the version constraint of
|
150
|
+
# this instance of Location.
|
151
|
+
#
|
152
|
+
# @return [Boolean]
|
153
|
+
def validate_cached(cached_cookbook)
|
154
|
+
unless version_constraint.satisfies?(cached_cookbook.version)
|
155
|
+
raise ConstraintNotSatisfied, "A cookbook satisfying '#{name}' (#{version_constraint}) not found at #{self}"
|
156
|
+
end
|
157
|
+
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def set_downloaded_status(state)
|
164
|
+
@downloaded_status = state
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
Dir["#{File.dirname(__FILE__)}/locations/*.rb"].sort.each do |path|
|
170
|
+
require "berkshelf/locations/#{File.basename(path, '.rb')}"
|
171
|
+
end
|