berkshelf 0.1.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.
- data/.gitignore +20 -0
- data/.rbenv-version +1 -0
- data/Gemfile +3 -0
- data/Guardfile +23 -0
- data/LICENSE +22 -0
- data/README.rdoc +102 -0
- data/Thorfile +47 -0
- data/berkshelf.gemspec +39 -0
- data/features/config.sample.yml +3 -0
- data/features/init_command.feature +40 -0
- data/features/install.feature +55 -0
- data/features/lockfile.feature +22 -0
- data/features/step_definitions/chef_server_steps.rb +13 -0
- data/features/step_definitions/cli_steps.rb +56 -0
- data/features/step_definitions/filesystem_steps.rb +79 -0
- data/features/support/env.rb +55 -0
- data/features/update.feature +23 -0
- data/features/upload_command.feature +43 -0
- data/features/without.feature +25 -0
- data/lib/berkshelf.rb +90 -0
- data/lib/berkshelf/berksfile.rb +170 -0
- data/lib/berkshelf/cached_cookbook.rb +253 -0
- data/lib/berkshelf/cookbook_source.rb +139 -0
- data/lib/berkshelf/cookbook_source/git_location.rb +54 -0
- data/lib/berkshelf/cookbook_source/path_location.rb +27 -0
- data/lib/berkshelf/cookbook_source/site_location.rb +206 -0
- data/lib/berkshelf/cookbook_store.rb +77 -0
- data/lib/berkshelf/core_ext.rb +3 -0
- data/lib/berkshelf/core_ext/file.rb +14 -0
- data/lib/berkshelf/core_ext/kernel.rb +33 -0
- data/lib/berkshelf/core_ext/pathname.rb +24 -0
- data/lib/berkshelf/downloader.rb +92 -0
- data/lib/berkshelf/dsl.rb +39 -0
- data/lib/berkshelf/errors.rb +20 -0
- data/lib/berkshelf/generator_files/Berksfile.erb +3 -0
- data/lib/berkshelf/generator_files/chefignore +44 -0
- data/lib/berkshelf/git.rb +70 -0
- data/lib/berkshelf/init_generator.rb +38 -0
- data/lib/berkshelf/lockfile.rb +42 -0
- data/lib/berkshelf/resolver.rb +176 -0
- data/lib/berkshelf/tx_result.rb +12 -0
- data/lib/berkshelf/tx_result_set.rb +37 -0
- data/lib/berkshelf/uploader.rb +153 -0
- data/lib/berkshelf/version.rb +3 -0
- data/lib/chef/knife/berks_init.rb +29 -0
- data/lib/chef/knife/berks_install.rb +27 -0
- data/lib/chef/knife/berks_update.rb +23 -0
- data/lib/chef/knife/berks_upload.rb +39 -0
- data/spec/fixtures/Berksfile +3 -0
- data/spec/fixtures/cookbooks/example_cookbook-0.5.0/README.md +12 -0
- data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +6 -0
- data/spec/fixtures/cookbooks/example_cookbook-0.5.0/recipes/default.rb +8 -0
- data/spec/fixtures/cookbooks/example_cookbook/README.md +12 -0
- data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +6 -0
- data/spec/fixtures/cookbooks/example_cookbook/recipes/default.rb +8 -0
- data/spec/fixtures/cookbooks/invalid_ruby_files-1.0.0/recipes/default.rb +1 -0
- data/spec/fixtures/cookbooks/invalid_template_files-1.0.0/templates/default/broken.erb +1 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/README.md +77 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/attributes/default.rb +65 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/definitions/nginx_site.rb +35 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/files/default/mime.types +73 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/files/ubuntu/mime.types +73 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/libraries/nginxlib.rb +1 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/metadata.rb +91 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/providers/defprovider.rb +1 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/recipes/default.rb +59 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/resources/defresource.rb +1 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/nginx.pill.erb +15 -0
- data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/plugins/nginx.rb.erb +66 -0
- data/spec/fixtures/lockfile_spec/with_lock/Berksfile +1 -0
- data/spec/fixtures/lockfile_spec/without_lock/Berksfile.lock +5 -0
- data/spec/spec_helper.rb +92 -0
- data/spec/support/chef_api.rb +27 -0
- data/spec/support/matchers/file_system_matchers.rb +115 -0
- data/spec/support/matchers/filepath_matchers.rb +19 -0
- data/spec/unit/berkshelf/cached_cookbook_spec.rb +420 -0
- data/spec/unit/berkshelf/cookbook_source/git_location_spec.rb +59 -0
- data/spec/unit/berkshelf/cookbook_source/path_location_spec.rb +34 -0
- data/spec/unit/berkshelf/cookbook_source/site_location_spec.rb +166 -0
- data/spec/unit/berkshelf/cookbook_source_spec.rb +194 -0
- data/spec/unit/berkshelf/cookbook_store_spec.rb +71 -0
- data/spec/unit/berkshelf/cookbookfile_spec.rb +160 -0
- data/spec/unit/berkshelf/downloader_spec.rb +82 -0
- data/spec/unit/berkshelf/dsl_spec.rb +42 -0
- data/spec/unit/berkshelf/git_spec.rb +63 -0
- data/spec/unit/berkshelf/init_generator_spec.rb +52 -0
- data/spec/unit/berkshelf/lockfile_spec.rb +25 -0
- data/spec/unit/berkshelf/resolver_spec.rb +126 -0
- data/spec/unit/berkshelf/tx_result_set_spec.rb +77 -0
- data/spec/unit/berkshelf/tx_result_spec.rb +21 -0
- data/spec/unit/berkshelf/uploader_spec.rb +71 -0
- data/spec/unit/berkshelf_spec.rb +29 -0
- metadata +411 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
module Berkshelf
|
|
2
|
+
class CookbookSource
|
|
3
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
|
4
|
+
class SiteLocation
|
|
5
|
+
include Location
|
|
6
|
+
|
|
7
|
+
attr_reader :api_uri
|
|
8
|
+
attr_accessor :version_constraint
|
|
9
|
+
|
|
10
|
+
OPSCODE_COMMUNITY_API = 'http://cookbooks.opscode.com/api/v1/cookbooks'.freeze
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
# @param [String] target
|
|
14
|
+
# file path to the tar.gz archive on disk
|
|
15
|
+
# @param [String] destination
|
|
16
|
+
# file path to extract the contents of the target to
|
|
17
|
+
def unpack(target, destination)
|
|
18
|
+
Archive::Tar::Minitar.unpack(Zlib::GzipReader.new(File.open(target, 'rb')), destination)
|
|
19
|
+
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]
|
|
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
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def initialize(name, options = {})
|
|
55
|
+
options[:site] ||= OPSCODE_COMMUNITY_API
|
|
56
|
+
|
|
57
|
+
@name = name
|
|
58
|
+
@version_constraint = options[:version_constraint]
|
|
59
|
+
@api_uri = options[:site]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def download(destination)
|
|
63
|
+
version, uri = target_version
|
|
64
|
+
remote_file = rest.get_rest(uri)['file']
|
|
65
|
+
downloaded_tf = rest.get_rest(remote_file, true)
|
|
66
|
+
|
|
67
|
+
dir = Dir.mktmpdir
|
|
68
|
+
cb_path = File.join(destination, "#{name}-#{version}")
|
|
69
|
+
|
|
70
|
+
self.class.unpack(downloaded_tf.path, dir)
|
|
71
|
+
FileUtils.mv(File.join(dir, name), cb_path, :force => true)
|
|
72
|
+
|
|
73
|
+
cb_path
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [Array]
|
|
77
|
+
# an Array where the first element is a DepSelector::Version representing the latest version of
|
|
78
|
+
# the Cookbook and the second element is the URI to where the corrosponding version of the
|
|
79
|
+
# Cookbook can be downloaded from
|
|
80
|
+
#
|
|
81
|
+
# Example:
|
|
82
|
+
# [ 0.101.2, "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_2" ]
|
|
83
|
+
def version(version_string)
|
|
84
|
+
quietly {
|
|
85
|
+
result = rest.get_rest("#{name}/versions/#{uri_escape_version(version_string)}")
|
|
86
|
+
dep_ver = DepSelector::Version.new(result['version'])
|
|
87
|
+
|
|
88
|
+
[ dep_ver, result['file'] ]
|
|
89
|
+
}
|
|
90
|
+
rescue Net::HTTPServerException => e
|
|
91
|
+
if e.response.code == "404"
|
|
92
|
+
raise CookbookNotFound, "Cookbook name: '#{name}' version: '#{version_string}' not found at site: '#{api_uri}'"
|
|
93
|
+
else
|
|
94
|
+
raise
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @return [Hash]
|
|
99
|
+
# a hash where the keys are a DepSelector::Version representing a Cookbook version
|
|
100
|
+
# number and their values are the URI the Cookbook of the corrosponding version can
|
|
101
|
+
# be downloaded from
|
|
102
|
+
#
|
|
103
|
+
# Example:
|
|
104
|
+
# {
|
|
105
|
+
# 0.101.2 => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_2",
|
|
106
|
+
# 0.101.0 => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_0",
|
|
107
|
+
# 0.100.2 => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_100_2",
|
|
108
|
+
# 0.100.0 => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_100_0"
|
|
109
|
+
# }
|
|
110
|
+
def versions
|
|
111
|
+
versions = Hash.new
|
|
112
|
+
quietly {
|
|
113
|
+
rest.get_rest(name)['versions'].each do |uri|
|
|
114
|
+
version_string = version_from_uri(File.basename(uri))
|
|
115
|
+
version = DepSelector::Version.new(version_string)
|
|
116
|
+
|
|
117
|
+
versions[version] = uri
|
|
118
|
+
end
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
versions
|
|
122
|
+
rescue Net::HTTPServerException => e
|
|
123
|
+
if e.response.code == "404"
|
|
124
|
+
raise CookbookNotFound, "Cookbook '#{name}' not found at site: '#{api_uri}'"
|
|
125
|
+
else
|
|
126
|
+
raise
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# @return [Array]
|
|
131
|
+
# an array where the first element is a DepSelector::Version representing the latest version of
|
|
132
|
+
# the Cookbook and the second element is the URI to where the corrosponding version of the
|
|
133
|
+
# Cookbook can be downloaded from
|
|
134
|
+
#
|
|
135
|
+
# Example:
|
|
136
|
+
# [ 0.101.2 => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_2" ]
|
|
137
|
+
def latest_version
|
|
138
|
+
quietly {
|
|
139
|
+
uri = rest.get_rest(name)['latest_version']
|
|
140
|
+
version_string = version_from_uri(uri)
|
|
141
|
+
dep_ver = DepSelector::Version.new(version_string)
|
|
142
|
+
|
|
143
|
+
[ dep_ver, uri ]
|
|
144
|
+
}
|
|
145
|
+
rescue Net::HTTPServerException => e
|
|
146
|
+
if e.response.code == "404"
|
|
147
|
+
raise CookbookNotFound, "Cookbook '#{name}' not found at site: '#{api_uri}'"
|
|
148
|
+
else
|
|
149
|
+
raise
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def api_uri=(uri)
|
|
154
|
+
@rest = nil
|
|
155
|
+
@api_uri = uri
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def to_s
|
|
159
|
+
"site: '#{api_uri}'"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
private
|
|
163
|
+
|
|
164
|
+
def rest
|
|
165
|
+
@rest ||= Chef::REST.new(api_uri, false, false)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def uri_escape_version(version)
|
|
169
|
+
version.gsub('.', '_')
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def version_from_uri(latest_version)
|
|
173
|
+
File.basename(latest_version).gsub('_', '.')
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# @return [Array]
|
|
177
|
+
# an Array where the first element is a DepSelector::Version and the second element is
|
|
178
|
+
# the URI to where the corrosponding version of the Cookbook can be downloaded from.
|
|
179
|
+
#
|
|
180
|
+
#
|
|
181
|
+
# The version is determined by the value of the version_constraint attribute of this
|
|
182
|
+
# instance of SiteLocation:
|
|
183
|
+
#
|
|
184
|
+
# If it is not set: the latest_version of the Cookbook will be returned
|
|
185
|
+
#
|
|
186
|
+
# If it is set: the return value will be determined by the version_constraint and the
|
|
187
|
+
# available versions will be solved
|
|
188
|
+
#
|
|
189
|
+
# Example:
|
|
190
|
+
# [ 0.101.2, "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_2" ]
|
|
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, "Could not satisfy version constraint: #{version_constraint} for Cookbook '#{name}'"
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
solution
|
|
200
|
+
else
|
|
201
|
+
latest_version
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
module Berkshelf
|
|
4
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
|
5
|
+
class CookbookStore
|
|
6
|
+
attr_reader :storage_path
|
|
7
|
+
|
|
8
|
+
# Create a new instance of CookbookStore with the given
|
|
9
|
+
# storage_path.
|
|
10
|
+
#
|
|
11
|
+
# @param [String] storage_path
|
|
12
|
+
# local filesystem path to the location to be initialized
|
|
13
|
+
# as a CookbookStore.
|
|
14
|
+
def initialize(storage_path)
|
|
15
|
+
@storage_path = Pathname.new(storage_path)
|
|
16
|
+
initialize_filesystem
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Returns an instance of CachedCookbook representing the
|
|
20
|
+
# Cookbook of your given name and version.
|
|
21
|
+
#
|
|
22
|
+
# @param [String] name
|
|
23
|
+
# name of the Cookbook you want to retrieve
|
|
24
|
+
# @param [String] version
|
|
25
|
+
# version of the Cookbook you want to retrieve
|
|
26
|
+
#
|
|
27
|
+
# @return [Berkshelf::CachedCookbook]
|
|
28
|
+
def cookbook(name, version)
|
|
29
|
+
return nil unless downloaded?(name, version)
|
|
30
|
+
|
|
31
|
+
path = cookbook_path(name, version)
|
|
32
|
+
CachedCookbook.from_path(path)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Returns an array of all of the Cookbooks that have been cached
|
|
36
|
+
# to the storage_path of this instance of CookbookStore.
|
|
37
|
+
#
|
|
38
|
+
# @return [Array<Berkshelf::CachedCookbook>]
|
|
39
|
+
def cookbooks
|
|
40
|
+
[].tap do |cookbooks|
|
|
41
|
+
storage_path.each_child do |p|
|
|
42
|
+
cached_cookbook = CachedCookbook.from_path(p)
|
|
43
|
+
|
|
44
|
+
cookbooks << cached_cookbook if cached_cookbook
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Returns an expanded path to the location on disk where the Cookbook
|
|
50
|
+
# of the given name and version is located.
|
|
51
|
+
#
|
|
52
|
+
# @param [String] name
|
|
53
|
+
# @param [String] version
|
|
54
|
+
#
|
|
55
|
+
# @return [Pathname]
|
|
56
|
+
def cookbook_path(name, version)
|
|
57
|
+
storage_path.join("#{name}-#{version}")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns true if the Cookbook of the given name and verion is downloaded
|
|
61
|
+
# to this instance of CookbookStore.
|
|
62
|
+
#
|
|
63
|
+
# @param [String] name
|
|
64
|
+
# @param [String] version
|
|
65
|
+
#
|
|
66
|
+
# @return [Boolean]
|
|
67
|
+
def downloaded?(name, version)
|
|
68
|
+
cookbook_path(name, version).cookbook?
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def initialize_filesystem
|
|
74
|
+
FileUtils.mkdir_p(storage_path, :mode => 0755)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class File
|
|
2
|
+
class << self
|
|
3
|
+
# Returns true or false if the given path is a Chef Cookbook
|
|
4
|
+
#
|
|
5
|
+
# @param [#to_s] path
|
|
6
|
+
# path of directory to reflect on
|
|
7
|
+
#
|
|
8
|
+
# @return [Boolean]
|
|
9
|
+
def cookbook?(path)
|
|
10
|
+
File.exists?(File.join(path, "metadata.rb"))
|
|
11
|
+
end
|
|
12
|
+
alias_method :chef_cookbook?, :cookbook?
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# From ActiveSupport: https://github.com/rails/rails/blob/c2c8ef57d6f00d1c22743dc43746f95704d67a95/activesupport/lib/active_support/core_ext/kernel/reporting.rb#L39
|
|
2
|
+
|
|
3
|
+
require 'rbconfig'
|
|
4
|
+
|
|
5
|
+
module Kernel
|
|
6
|
+
# Silences any stream for the duration of the block.
|
|
7
|
+
#
|
|
8
|
+
# silence_stream(STDOUT) do
|
|
9
|
+
# puts 'This will never be seen'
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# puts 'But this will'
|
|
13
|
+
def silence_stream(stream)
|
|
14
|
+
old_stream = stream.dup
|
|
15
|
+
stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null')
|
|
16
|
+
stream.sync = true
|
|
17
|
+
yield
|
|
18
|
+
ensure
|
|
19
|
+
stream.reopen(old_stream)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Silences both STDOUT and STDERR, even for subprocesses.
|
|
23
|
+
#
|
|
24
|
+
# quietly { system 'bundle install' }
|
|
25
|
+
#
|
|
26
|
+
def quietly
|
|
27
|
+
silence_stream(STDOUT) do
|
|
28
|
+
silence_stream(STDERR) do
|
|
29
|
+
yield
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
class Pathname
|
|
2
|
+
# Returns true or false if the path of the instantiated
|
|
3
|
+
# Pathname or a parent directory contains a Tryhard Pack
|
|
4
|
+
# data directory.
|
|
5
|
+
#
|
|
6
|
+
# @return [Boolean]
|
|
7
|
+
def cookbook?
|
|
8
|
+
self.join('metadata.rb').exist?
|
|
9
|
+
end
|
|
10
|
+
alias_method :chef_cookbook?, :cookbook?
|
|
11
|
+
|
|
12
|
+
# Ascend the directory structure from the given path to find a
|
|
13
|
+
# the root of a Chef Cookbook. If no Cookbook is found, nil is returned
|
|
14
|
+
#
|
|
15
|
+
# @return [Pathname, nil]
|
|
16
|
+
def cookbook_root
|
|
17
|
+
self.ascend do |potential_root|
|
|
18
|
+
if potential_root.cookbook?
|
|
19
|
+
return potential_root
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
alias_method :chef_cookbook_root, :cookbook_root
|
|
24
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module Berkshelf
|
|
2
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
|
3
|
+
class Downloader
|
|
4
|
+
extend Forwardable
|
|
5
|
+
|
|
6
|
+
attr_reader :cookbook_store
|
|
7
|
+
attr_reader :queue
|
|
8
|
+
|
|
9
|
+
def_delegators :@cookbook_store, :storage_path
|
|
10
|
+
|
|
11
|
+
def initialize(cookbook_store)
|
|
12
|
+
@cookbook_store = cookbook_store
|
|
13
|
+
@queue = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Add a CookbookSource to the downloader's queue
|
|
17
|
+
#
|
|
18
|
+
# @param [Berkshelf::CookbookSource] source
|
|
19
|
+
#
|
|
20
|
+
# @return [Array<Berkshelf::CookbookSource>]
|
|
21
|
+
# the queue - an array of Berkshelf::CookbookSources
|
|
22
|
+
def enqueue(source)
|
|
23
|
+
unless validate_source(source)
|
|
24
|
+
raise ArgumentError, "Invalid CookbookSource: can only enqueue valid instances of CookbookSource."
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@queue << source
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Remove a CookbookSource from the downloader's queue
|
|
31
|
+
#
|
|
32
|
+
# @param [Berkshelf::CookbookSource] source
|
|
33
|
+
#
|
|
34
|
+
# @return [Berkshelf::CookbookSource]
|
|
35
|
+
# the CookbookSource removed from the queue
|
|
36
|
+
def dequeue(source)
|
|
37
|
+
@queue.delete(source)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Download each CookbookSource in the queue. Upon successful download
|
|
41
|
+
# of a CookbookSource it is removed from the queue. If a CookbookSource
|
|
42
|
+
# fails to download it remains in the queue.
|
|
43
|
+
#
|
|
44
|
+
# @return [TXResultSet]
|
|
45
|
+
def download_all
|
|
46
|
+
results = TXResultSet.new
|
|
47
|
+
|
|
48
|
+
queue.each do |source|
|
|
49
|
+
results.add_result download(source)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
results.success.each { |result| dequeue(result.source) }
|
|
53
|
+
|
|
54
|
+
results
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Downloads the given CookbookSource
|
|
58
|
+
#
|
|
59
|
+
# @param [CookbookSource] source
|
|
60
|
+
# the source to download
|
|
61
|
+
#
|
|
62
|
+
# @return [TXResult]
|
|
63
|
+
def download(source)
|
|
64
|
+
status, message = source.download(storage_path)
|
|
65
|
+
TXResult.new(status, message, source)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Downloads the given CookbookSource. Raises a DownloadFailure error
|
|
69
|
+
# if the download was not successful.
|
|
70
|
+
#
|
|
71
|
+
# @param [CookbookSource] source
|
|
72
|
+
# the source to download
|
|
73
|
+
#
|
|
74
|
+
# @return [TXResult]
|
|
75
|
+
def download!(source)
|
|
76
|
+
result = download(source)
|
|
77
|
+
raise DownloadFailure, result.message if result.failed?
|
|
78
|
+
|
|
79
|
+
result
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def downloaded?(source)
|
|
83
|
+
source.downloaded? || cookbook_store.downloaded?(source.name, source.local_version)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def validate_source(source)
|
|
89
|
+
source.is_a?(Berkshelf::CookbookSource)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|