berkshelf 1.2.0.rc1 → 1.2.1

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 (37) hide show
  1. data/CHANGELOG.md +8 -0
  2. data/Gemfile +1 -1
  3. data/README.md +2 -0
  4. data/berkshelf.gemspec +4 -3
  5. data/features/step_definitions/filesystem_steps.rb +0 -1
  6. data/generator_files/Gemfile.erb +0 -3
  7. data/generator_files/Vagrantfile.erb +13 -1
  8. data/lib/berkshelf.rb +0 -1
  9. data/lib/berkshelf/berksfile.rb +11 -4
  10. data/lib/berkshelf/cached_cookbook.rb +2 -257
  11. data/lib/berkshelf/chef.rb +0 -1
  12. data/lib/berkshelf/chef/config.rb +3 -0
  13. data/lib/berkshelf/chef/cookbook.rb +0 -2
  14. data/lib/berkshelf/community_rest.rb +31 -6
  15. data/lib/berkshelf/cookbook_source.rb +5 -1
  16. data/lib/berkshelf/errors.rb +24 -0
  17. data/lib/berkshelf/git.rb +49 -1
  18. data/lib/berkshelf/init_generator.rb +1 -1
  19. data/lib/berkshelf/locations/chef_api_location.rb +6 -3
  20. data/lib/berkshelf/locations/path_location.rb +2 -0
  21. data/lib/berkshelf/version.rb +1 -1
  22. data/spec/spec_helper.rb +9 -2
  23. data/spec/support/chef_api.rb +1 -10
  24. data/spec/unit/berkshelf/cached_cookbook_spec.rb +37 -458
  25. data/spec/unit/berkshelf/git_spec.rb +119 -9
  26. data/spec/unit/berkshelf/init_generator_spec.rb +0 -1
  27. metadata +30 -24
  28. data/lib/berkshelf/chef/cookbook/metadata.rb +0 -556
  29. data/lib/berkshelf/chef/cookbook/syntax_check.rb +0 -158
  30. data/lib/berkshelf/chef/digester.rb +0 -67
  31. data/lib/berkshelf/mixin/checksum.rb +0 -16
  32. data/lib/berkshelf/mixin/params_validate.rb +0 -218
  33. data/lib/berkshelf/mixin/shell_out.rb +0 -23
  34. data/lib/berkshelf/uploader.rb +0 -80
  35. data/spec/unit/berkshelf/uploader_spec.rb +0 -27
  36. data/spec/unit/chef/cookbook/metadata_spec.rb +0 -5
  37. data/spec/unit/chef/digester_spec.rb +0 -41
@@ -1,3 +1,11 @@
1
+ # 1.2.0
2
+ - Remove Vagrant as a gem dependency
3
+ - Remove Chef as a gem dependency
4
+ - Add retries to downloads/uploads
5
+ - Speed optimizations to resolver
6
+ - Speed optimizations to downloading cookbooks
7
+ - Speed optimizations to uploading cookbooks
8
+
1
9
  # 1.1.0
2
10
  ## new/improved commands
3
11
  - `berks show` command: display the file path for the given cookbook's current version resolved by your Berksfile
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Berkshelf
2
2
  =========
3
+ [![Gem Version](https://badge.fury.io/rb/berkshelf.png)](http://badge.fury.io/rb/berkshelf)
3
4
  [![Build Status](https://travis-ci.org/RiotGames/berkshelf.png?branch=master)](https://travis-ci.org/RiotGames/berkshelf)
5
+ [![Dependency Status](https://gemnasium.com/RiotGames/berkshelf.png)](https://gemnasium.com/RiotGames/berkshelf)
4
6
  [![Code Climate](https://codeclimate.com/github/RiotGames/berkshelf.png)](https://codeclimate.com/github/RiotGames/berkshelf)
5
7
 
6
8
  Manage a Cookbook or an Application's Cookbook dependencies
@@ -32,14 +32,15 @@ Gem::Specification.new do |s|
32
32
  s.add_dependency 'mixlib-shellout'
33
33
  s.add_dependency 'mixlib-config'
34
34
  s.add_dependency 'faraday', '>= 0.8.5'
35
- s.add_dependency 'ridley', '>= 0.7.0.rc4'
36
- s.add_dependency 'chozo', '>= 0.5.0'
37
- s.add_dependency 'hashie'
35
+ s.add_dependency 'ridley', '>= 0.8.3'
36
+ s.add_dependency 'chozo', '>= 0.6.1'
37
+ s.add_dependency 'hashie', '>= 2.0.2'
38
38
  s.add_dependency 'minitar'
39
39
  s.add_dependency 'json', '>= 1.5.0'
40
40
  s.add_dependency 'multi_json', '~> 1.5'
41
41
  s.add_dependency 'solve', '>= 0.4.2'
42
42
  s.add_dependency 'thor', '~> 0.16.0'
43
+ s.add_dependency 'retryable'
43
44
 
44
45
  # Vagrant 1-0-stable compatability locks
45
46
  s.add_dependency 'moneta', '~> 0.6.0'
@@ -106,7 +106,6 @@ Then /^I should have a new cookbook skeleton "(.*?)"$/ do |name|
106
106
  end
107
107
  file "Gemfile" do
108
108
  contains "gem 'berkshelf'"
109
- contains "gem 'vagrant'"
110
109
  end
111
110
  file "metadata.rb"
112
111
  file "README.md"
@@ -7,6 +7,3 @@ gem 'thor-foodcritic'
7
7
  <% if options[:scmversion] -%>
8
8
  gem 'thor-scmversion'
9
9
  <% end -%>
10
- <% unless options[:skip_vagrant] -%>
11
- gem 'vagrant', '~> 1.0.5'
12
- <% end -%>
@@ -1,4 +1,16 @@
1
- require 'berkshelf/vagrant'
1
+ begin
2
+ require 'berkshelf/vagrant'
3
+ rescue LoadError
4
+ puts "[WARNING] Berkshelf not found in your Vagrant's RubyGems but your Vagrantfile is attempting"
5
+ puts "[WARNING] to require the Berkshelf Vagrant plugin! Install the Berkshelf Vagrant plugin or"
6
+ puts "[WARNING] remove the 'require \"berkshelf/vagrant\"' line from the top of your Vagrantfile."
7
+ puts ""
8
+ puts "If you installed Vagrant by RubyGems:"
9
+ puts " Install Berkshelf by running: \"gem install berkshelf\""
10
+ puts "If you installed Vagrant by one of the pre-packaged installers:"
11
+ puts " Install Berkshelf by running: \"vagrant gem install berkshelf\""
12
+ puts ""
13
+ end
2
14
 
3
15
  Vagrant::Config.run do |config|
4
16
  # All Vagrant configuration is done here. The most common configuration
@@ -48,7 +48,6 @@ module Berkshelf
48
48
  autoload :Mixin, 'berkshelf/mixin'
49
49
  autoload :Resolver, 'berkshelf/resolver'
50
50
  autoload :UI, 'berkshelf/ui'
51
- autoload :Uploader, 'berkshelf/uploader'
52
51
 
53
52
  require 'berkshelf/location'
54
53
 
@@ -183,7 +183,7 @@ module Berkshelf
183
183
  raise CookbookNotFound, "No 'metadata.rb' found at #{path}"
184
184
  end
185
185
 
186
- metadata = Berkshelf::Chef::Cookbook::Metadata.from_file(metadata_file.to_s)
186
+ metadata = Ridley::Chef::Cookbook::Metadata.from_file(metadata_file.to_s)
187
187
 
188
188
  name = if metadata.name.empty? || metadata.name.nil?
189
189
  File.basename(File.dirname(metadata_file))
@@ -478,12 +478,17 @@ module Berkshelf
478
478
  #
479
479
  # @raise [UploadFailure] if you are uploading cookbooks with an invalid or not-specified client key
480
480
  def upload(options = {})
481
- uploader = Uploader.new(options)
481
+ conn = Ridley.new(options)
482
482
  solution = resolve(options)
483
+
483
484
  solution.each do |cb|
484
- Berkshelf.formatter.upload cb.cookbook_name, cb.version, options[:server_url]
485
- uploader.upload(cb, options)
485
+ upload_opts = options.dup
486
+ upload_opts[:name] = cb.cookbook_name
487
+
488
+ Berkshelf.formatter.upload cb.cookbook_name, cb.version, upload_opts[:server_url]
489
+ conn.cookbook.upload(cb.path, upload_opts)
486
490
  end
491
+
487
492
  if options[:skip_dependencies]
488
493
  missing_cookbooks = options.fetch(:cookbooks, nil) - solution.map(&:cookbook_name)
489
494
  unless missing_cookbooks.empty?
@@ -496,6 +501,8 @@ module Berkshelf
496
501
  msg = "Could not upload cookbooks: Missing Chef client key: '#{Berkshelf::Config.instance.chef.client_key}'."
497
502
  msg << " Generate or update your Berkshelf configuration that contains a valid path to a Chef client key."
498
503
  raise UploadFailure, msg
504
+ ensure
505
+ conn.terminate if conn && conn.alive?
499
506
  end
500
507
 
501
508
  # Finds a solution for the Berksfile and returns an array of CachedCookbooks.
@@ -1,29 +1,7 @@
1
1
  module Berkshelf
2
2
  # @author Jamie Winsor <reset@riotgames.com>
3
- class CachedCookbook
3
+ class CachedCookbook < Ridley::Chef::Cookbook
4
4
  class << self
5
- include Berkshelf::Mixin::Checksum
6
-
7
- # Creates a new instance of Berkshelf::CachedCookbook from a path on disk that
8
- # contains a Cookbook. The name of the Cookbook will be determined first by the
9
- # name attribute of the metadata.rb file if it is present. If the name attribute
10
- # has not been set the Cookbook name will be determined by the basename of the
11
- # given filepath.
12
- #
13
- # @param [#to_s] path
14
- # a path on disk to the location of a Cookbook
15
- #
16
- # @return [Berkshelf::CachedCookbook]
17
- def from_path(path)
18
- path = Pathname.new(path)
19
- metadata = Berkshelf::Chef::Cookbook::Metadata.from_file(path.join('metadata.rb'))
20
-
21
- name = metadata.name.empty? ? File.basename(path) : metadata.name
22
- metadata.name(name) if metadata.name.empty?
23
-
24
- new(name, path, metadata)
25
- end
26
-
27
5
  # @param [#to_s] path
28
6
  # a path on disk to the location of a Cookbook downloaded by the Downloader
29
7
  #
@@ -35,248 +13,15 @@ module Berkshelf
35
13
  cached_name = File.basename(path.to_s).slice(DIRNAME_REGEXP, 1)
36
14
  return nil if cached_name.nil?
37
15
 
38
- metadata = Berkshelf::Chef::Cookbook::Metadata.from_file(path.join('metadata.rb'))
39
- metadata.name(cached_name) if metadata.name.empty?
40
-
41
- new(cached_name, path, metadata)
42
- end
43
-
44
- # @param [String] filepath
45
- # a path on disk to the location of a file to checksum
46
- #
47
- # @return [String]
48
- # a checksum that can be used to uniquely identify the file understood
49
- # by a Chef Server.
50
- def checksum(filepath)
51
- Berkshelf::Chef::Digester.md5_checksum_for_file(filepath)
16
+ from_path(path, name: cached_name)
52
17
  end
53
18
  end
54
19
 
55
20
  DIRNAME_REGEXP = /^(.+)-(.+)$/
56
- CHEF_TYPE = "cookbook_version".freeze
57
- CHEF_JSON_CLASS = "Chef::CookbookVersion".freeze
58
-
59
- extend Forwardable
60
-
61
- attr_reader :cookbook_name
62
- attr_reader :path
63
- attr_reader :metadata
64
-
65
- # @return [Hashie::Mash]
66
- # a Hashie::Mash containing Cookbook file category names as keys and an Array of Hashes
67
- # containing metadata about the files belonging to that category. This is used
68
- # to communicate what a Cookbook looks like when uploading to a Chef Server.
69
- #
70
- # example:
71
- # {
72
- # :recipes => [
73
- # {
74
- # name: "default.rb",
75
- # path: "recipes/default.rb",
76
- # checksum: "fb1f925dcd5fc4ebf682c4442a21c619",
77
- # specificity: "default"
78
- # }
79
- # ]
80
- # ...
81
- # ...
82
- # }
83
- attr_reader :manifest
84
-
85
- def_delegator :@metadata, :version
86
-
87
- def initialize(name, path, metadata)
88
- @cookbook_name = name
89
- @path = Pathname.new(path)
90
- @metadata = metadata
91
- @files = Array.new
92
- @manifest = Hashie::Mash.new(
93
- recipes: Array.new,
94
- definitions: Array.new,
95
- libraries: Array.new,
96
- attributes: Array.new,
97
- files: Array.new,
98
- templates: Array.new,
99
- resources: Array.new,
100
- providers: Array.new,
101
- root_files: Array.new
102
- )
103
-
104
- load_files
105
- end
106
-
107
- # @return [String]
108
- # the name of the cookbook and the version number separated by a dash (-).
109
- #
110
- # example:
111
- # "nginx-0.101.2"
112
- def name
113
- "#{cookbook_name}-#{version}"
114
- end
115
21
 
116
22
  # @return [Hash]
117
23
  def dependencies
118
24
  metadata.recommendations.merge(metadata.dependencies)
119
25
  end
120
-
121
- # @return [Hash]
122
- # an hash containing the checksums and expanded file paths of all of the
123
- # files found in the instance of CachedCookbook
124
- #
125
- # example:
126
- # {
127
- # "da97c94bb6acb2b7900cbf951654fea3" => "/Users/reset/.berkshelf/nginx-0.101.2/README.md"
128
- # }
129
- def checksums
130
- {}.tap do |checksums|
131
- files.each do |file|
132
- checksums[self.class.checksum(file)] = file
133
- end
134
- end
135
- end
136
-
137
- # @param [Symbol] category
138
- # the category of file to generate metadata about
139
- # @param [String] target
140
- # the filepath to the file to get metadata information about
141
- #
142
- # @return [Hash]
143
- # a Hash containing a name, path, checksum, and specificity key representing the
144
- # metadata about a file contained in a Cookbook. This metadata is used when
145
- # uploading a Cookbook's files to a Chef Server.
146
- #
147
- # example:
148
- # {
149
- # name: "default.rb",
150
- # path: "recipes/default.rb",
151
- # checksum: "fb1f925dcd5fc4ebf682c4442a21c619",
152
- # specificity: "default"
153
- # }
154
- def file_metadata(category, target)
155
- target = Pathname.new(target)
156
-
157
- {
158
- name: target.basename.to_s,
159
- path: target.relative_path_from(path).to_s,
160
- checksum: self.class.checksum(target),
161
- specificity: file_specificity(category, target)
162
- }
163
- end
164
-
165
- # Validates that this instance of CachedCookbook points to a valid location on disk that
166
- # contains a cookbook which passes a Ruby and template syntax check. Raises an error if
167
- # these assertions are not true.
168
- #
169
- # @return [Boolean]
170
- # returns true if Cookbook is valid
171
- def validate!
172
- raise CookbookNotFound, "No Cookbook found at: #{path}" unless path.exist?
173
-
174
- unless quietly { syntax_checker.validate_ruby_files }
175
- raise CookbookSyntaxError, "Invalid ruby files in cookbook: #{name} (#{version})."
176
- end
177
- unless quietly { syntax_checker.validate_templates }
178
- raise CookbookSyntaxError, "Invalid template files in cookbook: #{name} (#{version})."
179
- end
180
-
181
- true
182
- end
183
-
184
- def to_hash
185
- result = manifest.dup
186
- result['chef_type'] = 'cookbook_version'
187
- result['name'] = name
188
- result['cookbook_name'] = cookbook_name
189
- result['version'] = version
190
- result['metadata'] = metadata
191
- result['chef_type']
192
- result
193
- end
194
-
195
- def to_json(*a)
196
- result = self.to_hash
197
- result['json_class'] = chef_json_class
198
- result['frozen?'] = false
199
- result.to_json(*a)
200
- end
201
-
202
- def to_s
203
- "#{cookbook_name} (#{version}) '#{path}'"
204
- end
205
-
206
- def <=>(other_cookbook)
207
- [self.cookbook_name, self.version] <=> [other_cookbook.cookbook_name, other_cookbook.version]
208
- end
209
-
210
- private
211
-
212
- attr_reader :files
213
-
214
- def chef_type
215
- CHEF_TYPE
216
- end
217
-
218
- def chef_json_class
219
- CHEF_JSON_CLASS
220
- end
221
-
222
- def syntax_checker
223
- @syntax_checker ||= Berkshelf::Chef::Cookbook::SyntaxCheck.new(path.to_s)
224
- end
225
-
226
- def load_files
227
- load_shallow(:recipes, 'recipes', '*.rb')
228
- load_shallow(:definitions, 'definitions', '*.rb')
229
- load_shallow(:libraries, 'libraries', '*.rb')
230
- load_shallow(:attributes, 'attributes', '*.rb')
231
- load_recursively(:files, "files", "*")
232
- load_recursively(:templates, "templates", "*")
233
- load_recursively(:resources, "resources", "*.rb")
234
- load_recursively(:providers, "providers", "*.rb")
235
- load_root
236
- end
237
-
238
- def load_root
239
- [].tap do |files|
240
- Dir.glob(path.join('*'), File::FNM_DOTMATCH).each do |file|
241
- next if File.directory?(file)
242
- @files << file
243
- @manifest[:root_files] << file_metadata(:root_files, file)
244
- end
245
- end
246
- end
247
-
248
- def load_recursively(category, category_dir, glob)
249
- [].tap do |files|
250
- file_spec = path.join(category_dir, '**', glob)
251
- Dir.glob(file_spec, File::FNM_DOTMATCH).each do |file|
252
- next if File.directory?(file)
253
- @files << file
254
- @manifest[category] << file_metadata(category, file)
255
- end
256
- end
257
- end
258
-
259
- def load_shallow(category, *path_glob)
260
- [].tap do |files|
261
- Dir[path.join(*path_glob)].each do |file|
262
- @files << file
263
- @manifest[category] << file_metadata(category, file)
264
- end
265
- end
266
- end
267
-
268
- # @param [Symbol] category
269
- # @param [Pathname] target
270
- #
271
- # @return [String]
272
- def file_specificity(category, target)
273
- case category
274
- when :files, :templates
275
- relpath = target.relative_path_from(path).to_s
276
- relpath.slice(/(.+)\/(.+)\/.+/, 2)
277
- else
278
- 'default'
279
- end
280
- end
281
26
  end
282
27
  end
@@ -6,6 +6,5 @@ module Berkshelf
6
6
  module Chef
7
7
  autoload :Config, 'berkshelf/chef/config'
8
8
  autoload :Cookbook, 'berkshelf/chef/cookbook'
9
- autoload :Digester, 'berkshelf/chef/digester'
10
9
  end
11
10
  end
@@ -1,4 +1,5 @@
1
1
  require 'socket'
2
+ require 'tmpdir'
2
3
  require 'berkshelf/mixin'
3
4
  require 'mixlib/config'
4
5
 
@@ -81,6 +82,8 @@ module Berkshelf::Chef
81
82
  cookbook_email "YOUR_EMAIL"
82
83
  cookbook_license "reserved"
83
84
 
85
+ knife Hash.new
86
+
84
87
  # history: prior to Chef 11, the cache implementation was based on
85
88
  # moneta and configured via cache_options[:path]. Knife configs
86
89
  # generated with Chef 11 will have `syntax_check_cache_path`, but older
@@ -2,7 +2,5 @@ module Berkshelf::Chef
2
2
  # @author Jamie Winsor <reset@riotgames.com>
3
3
  module Cookbook
4
4
  autoload :Chefignore, 'berkshelf/chef/cookbook/chefignore'
5
- autoload :Metadata, 'berkshelf/chef/cookbook/metadata'
6
- autoload :SyntaxCheck, 'berkshelf/chef/cookbook/syntax_check'
7
5
  end
8
6
  end
@@ -1,4 +1,5 @@
1
1
  require 'open-uri'
2
+ require 'retryable'
2
3
 
3
4
  module Berkshelf
4
5
  # @author Jamie Winsor <reset@riotgames.com>
@@ -32,13 +33,35 @@ module Berkshelf
32
33
 
33
34
  V1_API = 'http://cookbooks.opscode.com/api/v1/cookbooks'.freeze
34
35
 
36
+ # @return [String]
35
37
  attr_reader :api_uri
36
-
37
- def initialize(uri = V1_API)
38
- @api_uri = Addressable::URI.parse(uri)
38
+ # @return [Integer]
39
+ # how many retries to attempt on HTTP requests
40
+ attr_reader :retries
41
+ # @return [Float]
42
+ # time to wait between retries
43
+ attr_reader :retry_interval
44
+
45
+ # @param [String] uri (CommunityREST::V1_API)
46
+ # location of community site to connect to
47
+ #
48
+ # @option options [Integer] :retries (5)
49
+ # retry requests on 5XX failures
50
+ # @option options [Float] :retry_interval (0.5)
51
+ # how often we should pause between retries
52
+ def initialize(uri = V1_API, options = {})
53
+ options = options.reverse_merge(retries: 5, retry_interval: 0.5)
54
+ @api_uri = Addressable::URI.parse(uri)
55
+ @retries = options[:retries]
56
+ @retry_interval = options[:retry_interval]
39
57
 
40
58
  builder = Faraday::Builder.new do |b|
41
59
  b.response :json
60
+ b.request :retry,
61
+ max: @retries,
62
+ interval: @retry_interval,
63
+ exceptions: [Faraday::Error::TimeoutError]
64
+
42
65
  b.adapter :net_http
43
66
  end
44
67
 
@@ -123,10 +146,12 @@ module Berkshelf
123
146
  local = Tempfile.new('community-rest-stream')
124
147
  local.binmode
125
148
 
126
- open(target, 'rb', headers) do |remote|
127
- local.write(remote.read)
149
+ retryable(tries: retries, on: OpenURI::HTTPError, sleep: retry_interval) do
150
+ open(target, 'rb', headers) do |remote|
151
+ local.write(remote.read)
152
+ end
128
153
  end
129
-
154
+
130
155
  local
131
156
  ensure
132
157
  local.close(false) unless local.nil?