berkshelf 0.3.7 → 0.4.0.rc1

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