berkshelf 0.4.0.rc4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|