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
@@ -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
|