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