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
@@ -0,0 +1,252 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
|
+
class ChefAPILocation
|
4
|
+
class << self
|
5
|
+
# @param [String] node_name
|
6
|
+
#
|
7
|
+
# @return [Boolean]
|
8
|
+
def validate_node_name(node_name)
|
9
|
+
node_name.is_a?(String) && !node_name.empty?
|
10
|
+
end
|
11
|
+
|
12
|
+
# @raise [InvalidChefAPILocation]
|
13
|
+
#
|
14
|
+
# @see validate_node_name
|
15
|
+
def validate_node_name!(node_name)
|
16
|
+
unless validate_node_name(node_name)
|
17
|
+
raise InvalidChefAPILocation
|
18
|
+
end
|
19
|
+
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [String] client_key
|
24
|
+
#
|
25
|
+
# @return [Boolean]
|
26
|
+
def validate_client_key(client_key)
|
27
|
+
File.exists?(client_key)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @raise [InvalidChefAPILocation]
|
31
|
+
#
|
32
|
+
# @see validate_client_key
|
33
|
+
def validate_client_key!(client_key)
|
34
|
+
unless validate_client_key(client_key)
|
35
|
+
raise InvalidChefAPILocation
|
36
|
+
end
|
37
|
+
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param [String] uri
|
42
|
+
#
|
43
|
+
# @return [Boolean]
|
44
|
+
def validate_uri(uri)
|
45
|
+
uri =~ URI.regexp(['http', 'https'])
|
46
|
+
end
|
47
|
+
|
48
|
+
# @raise [InvalidChefAPILocation] if the given object is not a String containing a
|
49
|
+
# valid Chef API URI
|
50
|
+
#
|
51
|
+
# @see validate_uri
|
52
|
+
def validate_uri!(uri)
|
53
|
+
unless validate_uri(uri)
|
54
|
+
raise InvalidChefAPILocation, "'#{uri}' is not a valid Chef API URI."
|
55
|
+
end
|
56
|
+
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
include Location
|
62
|
+
|
63
|
+
location_key :chef_api
|
64
|
+
valid_options :node_name, :client_key
|
65
|
+
|
66
|
+
attr_reader :uri
|
67
|
+
attr_reader :node_name
|
68
|
+
attr_reader :client_key
|
69
|
+
|
70
|
+
# @param [#to_s] name
|
71
|
+
# @param [Solve::Constraint] version_constraint
|
72
|
+
# @param [Hash] options
|
73
|
+
#
|
74
|
+
# @option options [String, Symbol] :chef_api
|
75
|
+
# a URL to a Chef API. Alternatively the symbol :knife can be provided
|
76
|
+
# which will instantiate this location with the values found in your
|
77
|
+
# knife configuration.
|
78
|
+
# @option options [String] :node_name
|
79
|
+
# the name of the client to use to communicate with the Chef API.
|
80
|
+
# Default: Chef::Config[:node_name]
|
81
|
+
# @option options [String] :client_key
|
82
|
+
# the filepath to the authentication key for the client
|
83
|
+
# Default: Chef::Config[:client_key]
|
84
|
+
def initialize(name, version_constraint, options = {})
|
85
|
+
@name = name
|
86
|
+
@version_constraint = version_constraint
|
87
|
+
@downloaded_status = false
|
88
|
+
|
89
|
+
validate_options!(options)
|
90
|
+
|
91
|
+
if options[:chef_api] == :knife
|
92
|
+
begin
|
93
|
+
Berkshelf.load_config
|
94
|
+
rescue KnifeConfigNotFound => e
|
95
|
+
raise KnifeConfigNotFound, "A Knife config is required when ':knife' is given for the value of a 'chef_api' location. #{e}"
|
96
|
+
end
|
97
|
+
@node_name = Chef::Config[:node_name]
|
98
|
+
@client_key = Chef::Config[:client_key]
|
99
|
+
@uri = Chef::Config[:chef_server_url]
|
100
|
+
else
|
101
|
+
@node_name = options[:node_name]
|
102
|
+
@client_key = options[:client_key]
|
103
|
+
@uri = options[:chef_api]
|
104
|
+
end
|
105
|
+
|
106
|
+
@rest = Chef::REST.new(uri, node_name, client_key)
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param [#to_s] destination
|
110
|
+
#
|
111
|
+
# @return [Berkshelf::CachedCookbook]
|
112
|
+
def download(destination)
|
113
|
+
version, uri = target_version
|
114
|
+
cookbook = rest.get_rest(uri)
|
115
|
+
|
116
|
+
scratch = download_files(cookbook.manifest)
|
117
|
+
|
118
|
+
cb_path = File.join(destination, "#{name}-#{version}")
|
119
|
+
FileUtils.mv(scratch, cb_path, force: true)
|
120
|
+
|
121
|
+
cached = CachedCookbook.from_store_path(cb_path)
|
122
|
+
validate_cached(cached)
|
123
|
+
|
124
|
+
set_downloaded_status(true)
|
125
|
+
cached
|
126
|
+
end
|
127
|
+
|
128
|
+
# Returns a hash representing the cookbook versions on at a Chef API for location's cookbook.
|
129
|
+
# The keys are version strings and the values are URLs to download the cookbook version.
|
130
|
+
#
|
131
|
+
# @example
|
132
|
+
# {
|
133
|
+
# "0.101.2" => "https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.2",
|
134
|
+
# "0.101.5" => "https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.5"
|
135
|
+
# }
|
136
|
+
#
|
137
|
+
# @return [Hash]
|
138
|
+
def versions
|
139
|
+
{}.tap do |versions|
|
140
|
+
rest.get_rest("cookbooks/#{name}").each do |name, data|
|
141
|
+
data["versions"].each do |version_info|
|
142
|
+
versions[version_info["version"]] = version_info["url"]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
rescue Net::HTTPServerException => e
|
147
|
+
if e.response.code == "404"
|
148
|
+
raise CookbookNotFound, "Cookbook '#{name}' not found at chef_api: '#{uri}'"
|
149
|
+
else
|
150
|
+
raise
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Returns an array where the first element is a string representing the latest version of
|
155
|
+
# the Cookbook and the second element is the download URL for that version.
|
156
|
+
#
|
157
|
+
# @example
|
158
|
+
# [ "0.101.2" => "https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.2" ]
|
159
|
+
#
|
160
|
+
# @return [Array]
|
161
|
+
def latest_version
|
162
|
+
graph = Solve::Graph.new
|
163
|
+
versions.each do |version, url|
|
164
|
+
graph.artifacts(name, version)
|
165
|
+
end
|
166
|
+
graph.demands(name, ">= 0.0.0")
|
167
|
+
|
168
|
+
version = Solve.it!(graph)[name]
|
169
|
+
|
170
|
+
[ version, versions[version] ]
|
171
|
+
end
|
172
|
+
|
173
|
+
def to_s
|
174
|
+
"chef_api: '#{uri}'"
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
attr_reader :rest
|
180
|
+
|
181
|
+
# Returns an array containing the version and download URL for the cookbook version that
|
182
|
+
# should be downloaded for this location.
|
183
|
+
#
|
184
|
+
# @example
|
185
|
+
# [ "0.101.2" => "https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.2" ]
|
186
|
+
#
|
187
|
+
# @return [Array]
|
188
|
+
def target_version
|
189
|
+
if version_constraint
|
190
|
+
solution = self.class.solve_for_constraint(version_constraint, versions)
|
191
|
+
|
192
|
+
unless solution
|
193
|
+
raise NoSolution, "No cookbook version of '#{name}' found at #{self} that would satisfy constraint (#{version_constraint})."
|
194
|
+
end
|
195
|
+
|
196
|
+
solution
|
197
|
+
else
|
198
|
+
latest_version
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# Download all of the files in the given manifest to the given destination. If no destination
|
203
|
+
# is provided a temporary directory will be created and the files will be downloaded to there.
|
204
|
+
#
|
205
|
+
# @note
|
206
|
+
# the manifest Hash is the same manifest that you get by sending the manifest message to
|
207
|
+
# an instance of Chef::CookbookVersion.
|
208
|
+
#
|
209
|
+
# @param [Hash] manifest
|
210
|
+
# @param [String] destination
|
211
|
+
#
|
212
|
+
# @return [String]
|
213
|
+
# the path to the directory containing the files
|
214
|
+
def download_files(manifest, destination = Dir.mktmpdir)
|
215
|
+
Chef::CookbookVersion::COOKBOOK_SEGMENTS.each do |segment|
|
216
|
+
next unless manifest.has_key?(segment)
|
217
|
+
manifest[segment].each do |segment_file|
|
218
|
+
dest = File.join(destination, segment_file['path'].gsub('/', File::SEPARATOR))
|
219
|
+
FileUtils.mkdir_p(File.dirname(dest))
|
220
|
+
rest.sign_on_redirect = false
|
221
|
+
tempfile = rest.get_rest(segment_file['url'], true)
|
222
|
+
FileUtils.mv(tempfile.path, dest)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
destination
|
227
|
+
end
|
228
|
+
|
229
|
+
# Validates the options hash given to the constructor.
|
230
|
+
#
|
231
|
+
# @param [Hash] options
|
232
|
+
#
|
233
|
+
# @raise [InvalidChefAPILocation] if any of the options are missing or their values do not
|
234
|
+
# pass validation
|
235
|
+
def validate_options!(options)
|
236
|
+
if options[:chef_api] == :knife
|
237
|
+
return true
|
238
|
+
end
|
239
|
+
|
240
|
+
missing_options = [:node_name, :client_key] - options.keys
|
241
|
+
|
242
|
+
unless missing_options.empty?
|
243
|
+
missing_options.collect! { |opt| "'#{opt}'" }
|
244
|
+
raise InvalidChefAPILocation, "Source '#{name}' is a 'chef_api' location with a URL for it's value but is missing options: #{missing_options.join(', ')}."
|
245
|
+
end
|
246
|
+
|
247
|
+
self.class.validate_node_name!(options[:node_name])
|
248
|
+
self.class.validate_client_key!(options[:client_key])
|
249
|
+
self.class.validate_uri!(options[:chef_api])
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
|
+
class GitLocation
|
4
|
+
include Location
|
5
|
+
|
6
|
+
location_key :git
|
7
|
+
valid_options :ref, :branch, :tag
|
8
|
+
|
9
|
+
attr_accessor :uri
|
10
|
+
attr_accessor :branch
|
11
|
+
|
12
|
+
alias_method :ref, :branch
|
13
|
+
alias_method :tag, :branch
|
14
|
+
|
15
|
+
# @param [#to_s] name
|
16
|
+
# @param [Solve::Constraint] version_constraint
|
17
|
+
# @param [Hash] options
|
18
|
+
#
|
19
|
+
# @option options [String] :git
|
20
|
+
# the Git URL to clone
|
21
|
+
# @option options [String] :ref
|
22
|
+
# the commit hash or an alias to a commit hash to clone
|
23
|
+
# @option options [String] :branch
|
24
|
+
# same as ref
|
25
|
+
# @option options [String] :tag
|
26
|
+
# same as tag
|
27
|
+
def initialize(name, version_constraint, options = {})
|
28
|
+
@name = name
|
29
|
+
@version_constraint = version_constraint
|
30
|
+
@uri = options[:git]
|
31
|
+
@branch = options[:branch] || options[:ref] || options[:tag]
|
32
|
+
|
33
|
+
Git.validate_uri!(@uri)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [#to_s] destination
|
37
|
+
#
|
38
|
+
# @return [Berkshelf::CachedCookbook]
|
39
|
+
def download(destination)
|
40
|
+
tmp_clone = Dir.mktmpdir
|
41
|
+
::Berkshelf::Git.clone(uri, tmp_clone)
|
42
|
+
::Berkshelf::Git.checkout(tmp_clone, branch) if branch
|
43
|
+
unless branch
|
44
|
+
self.branch = ::Berkshelf::Git.rev_parse(tmp_clone)
|
45
|
+
end
|
46
|
+
|
47
|
+
unless File.chef_cookbook?(tmp_clone)
|
48
|
+
msg = "Cookbook '#{name}' not found at git: #{uri}"
|
49
|
+
msg << " with branch '#{branch}'" if branch
|
50
|
+
raise CookbookNotFound, msg
|
51
|
+
end
|
52
|
+
|
53
|
+
cb_path = File.join(destination, "#{self.name}-#{Git.rev_parse(tmp_clone)}")
|
54
|
+
FileUtils.rm_rf(cb_path)
|
55
|
+
FileUtils.mv(tmp_clone, cb_path, force: true)
|
56
|
+
|
57
|
+
cached = CachedCookbook.from_store_path(cb_path)
|
58
|
+
validate_cached(cached)
|
59
|
+
|
60
|
+
set_downloaded_status(true)
|
61
|
+
cached
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_s
|
65
|
+
s = "git: '#{uri}'"
|
66
|
+
s << " with branch: '#{branch}'" if branch
|
67
|
+
s
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def git
|
73
|
+
@git ||= Berkshelf::Git.new(uri)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
|
+
class PathLocation
|
4
|
+
include Location
|
5
|
+
|
6
|
+
location_key :path
|
7
|
+
|
8
|
+
attr_accessor :path
|
9
|
+
|
10
|
+
# @param [#to_s] name
|
11
|
+
# @param [Solve::Constraint] version_constraint
|
12
|
+
# @param [Hash] options
|
13
|
+
#
|
14
|
+
# @option options [String] :path
|
15
|
+
# a filepath to the cookbook on your local disk
|
16
|
+
def initialize(name, version_constraint, options = {})
|
17
|
+
@name = name
|
18
|
+
@version_constraint = version_constraint
|
19
|
+
@path = File.expand_path(options[:path])
|
20
|
+
set_downloaded_status(true)
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [#to_s] destination
|
24
|
+
#
|
25
|
+
# @return [Berkshelf::CachedCookbook]
|
26
|
+
def download(destination)
|
27
|
+
cached = CachedCookbook.from_path(path)
|
28
|
+
validate_cached(cached)
|
29
|
+
|
30
|
+
set_downloaded_status(true)
|
31
|
+
cached
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
"path: '#{path}'"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
|
+
class SiteLocation
|
4
|
+
include Location
|
5
|
+
|
6
|
+
location_key :site
|
7
|
+
|
8
|
+
attr_reader :api_uri
|
9
|
+
attr_accessor :version_constraint
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# @param [String] target
|
13
|
+
# file path to the tar.gz archive on disk
|
14
|
+
# @param [String] destination
|
15
|
+
# file path to extract the contents of the target to
|
16
|
+
def unpack(target, destination)
|
17
|
+
Archive::Tar::Minitar.unpack(Zlib::GzipReader.new(File.open(target, 'rb')), destination)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param [#to_s] name
|
22
|
+
# @param [Solve::Constraint] version_constraint
|
23
|
+
# @param [Hash] options
|
24
|
+
#
|
25
|
+
# @option options [String, Symbol] :site
|
26
|
+
# a URL pointing to a community API endpoint. Alternatively the symbol :opscode can
|
27
|
+
# be provided to initialize a SiteLocation pointing to the Opscode Community Site.
|
28
|
+
def initialize(name, version_constraint, options = {})
|
29
|
+
@name = name
|
30
|
+
@version_constraint = version_constraint
|
31
|
+
|
32
|
+
@api_uri = if options[:site].nil? || options[:site] == :opscode
|
33
|
+
Location::OPSCODE_COMMUNITY_API
|
34
|
+
else
|
35
|
+
options[:site]
|
36
|
+
end
|
37
|
+
|
38
|
+
@rest = Chef::REST.new(api_uri, false, false)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param [#to_s] destination
|
42
|
+
#
|
43
|
+
# @return [Berkshelf::CachedCookbook]
|
44
|
+
def download(destination)
|
45
|
+
version, uri = target_version
|
46
|
+
remote_file = rest.get_rest(uri)['file']
|
47
|
+
|
48
|
+
downloaded_tf = rest.get_rest(remote_file, true)
|
49
|
+
|
50
|
+
dir = Dir.mktmpdir
|
51
|
+
cb_path = File.join(destination, "#{name}-#{version}")
|
52
|
+
|
53
|
+
self.class.unpack(downloaded_tf.path, dir)
|
54
|
+
FileUtils.mv(File.join(dir, name), cb_path, force: true)
|
55
|
+
|
56
|
+
cached = CachedCookbook.from_store_path(cb_path)
|
57
|
+
validate_cached(cached)
|
58
|
+
|
59
|
+
set_downloaded_status(true)
|
60
|
+
cached
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a hash of all the cookbook versions found at communite site URL for the cookbook
|
64
|
+
# name of this location.
|
65
|
+
#
|
66
|
+
# @example
|
67
|
+
# {
|
68
|
+
# "0.101.2" => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_2",
|
69
|
+
# "0.101.0" => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_0"
|
70
|
+
# }
|
71
|
+
#
|
72
|
+
# @return [Hash]
|
73
|
+
# a hash representing the cookbook versions on at a Chef API for location's cookbook.
|
74
|
+
# The keys are version strings and the values are URLs to download the cookbook version.
|
75
|
+
def versions
|
76
|
+
{}.tap do |versions|
|
77
|
+
rest.get_rest(name)['versions'].each do |uri|
|
78
|
+
version = version_from_uri(File.basename(uri))
|
79
|
+
|
80
|
+
versions[version] = uri
|
81
|
+
end
|
82
|
+
end
|
83
|
+
rescue Net::HTTPServerException => e
|
84
|
+
if e.response.code == "404"
|
85
|
+
raise CookbookNotFound, "Cookbook '#{name}' not found at site: '#{api_uri}'"
|
86
|
+
else
|
87
|
+
raise
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the latest version of the cookbook and it's download link.
|
92
|
+
#
|
93
|
+
# @example
|
94
|
+
# [ "0.101.2" => "https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.2" ]
|
95
|
+
#
|
96
|
+
# @return [Array]
|
97
|
+
# an array containing the version and download URL for the cookbook version that
|
98
|
+
# should be downloaded for this location.
|
99
|
+
def latest_version
|
100
|
+
quietly {
|
101
|
+
uri = rest.get_rest(name)['latest_version']
|
102
|
+
|
103
|
+
[ version_from_uri(uri), uri ]
|
104
|
+
}
|
105
|
+
rescue Net::HTTPServerException => e
|
106
|
+
if e.response.code == "404"
|
107
|
+
raise CookbookNotFound, "Cookbook '#{name}' not found at site: '#{api_uri}'"
|
108
|
+
else
|
109
|
+
raise
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def to_s
|
114
|
+
"site: '#{api_uri}'"
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
attr_reader :rest
|
120
|
+
|
121
|
+
def uri_escape_version(version)
|
122
|
+
version.gsub('.', '_')
|
123
|
+
end
|
124
|
+
|
125
|
+
def version_from_uri(latest_version)
|
126
|
+
File.basename(latest_version).gsub('_', '.')
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns an array containing the version and download URL for the cookbook version that
|
130
|
+
# should be downloaded for this location.
|
131
|
+
#
|
132
|
+
# @example
|
133
|
+
# [ "0.101.2" => "https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.2" ]
|
134
|
+
#
|
135
|
+
# @return [Array]
|
136
|
+
def target_version
|
137
|
+
if version_constraint
|
138
|
+
solution = self.class.solve_for_constraint(version_constraint, versions)
|
139
|
+
|
140
|
+
unless solution
|
141
|
+
raise NoSolution, "No cookbook version of '#{name}' found at #{self} that would satisfy constraint (#{version_constraint})."
|
142
|
+
end
|
143
|
+
|
144
|
+
solution
|
145
|
+
else
|
146
|
+
latest_version
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|