|
17 | 17 | # limitations under the License. |
18 | 18 | # |
19 | 19 |
|
| 20 | + |
| 21 | +# This class handles fetching of cookbook files based on specificity. A local |
| 22 | +# filesystem cache is maintained based on the checksum of files retrieved. |
20 | 23 | class Chef |
21 | 24 | class CookbookFileVendor |
22 | | - def get_file(node, cookbook, segment, filename) |
| 25 | + def initialize |
| 26 | + @cookbook_cache = Hash.new |
| 27 | + @checksum_cache = Chef::Cache::FileCacheByChecksum.new |
| 28 | + end |
| 29 | + |
| 30 | + # Gets the full pathname for the given cookbook part, returned in specificity |
| 31 | + # preference order. |
| 32 | + def get_filename(node, cookbook_name, segment, filename) |
| 33 | + checksum = get_preferred_checksum(node, cookbook_name, segment, filename) |
| 34 | + raise Chef::Exceptions::FileNotFound, "missing file #{segment}/#{filename} for cookbook #{cookbook_name}" unless checksum |
| 35 | + |
| 36 | + checksum_filename = @checksum_cache.get_path(checksum) |
| 37 | + unless checksum_filename |
| 38 | + # We don't have that checksum in the cache. Fetch it. |
| 39 | + # TODO: timh: 2010-4-7: Make this download streaming instead of loading it all in memory |
| 40 | + url = "/cookbooks/#{cookbook_name}/#{cookbook_version}/#{checksum}" |
| 41 | + |
| 42 | + # Second argument is true, so we treat the result as raw, not JSON |
| 43 | + tempfile = rest.get_rest(url, true) |
| 44 | + checksum_filename = @checksum_cache.put(checksum, tempfile.path) |
| 45 | + tempfile.close |
| 46 | + end |
| 47 | + checksum_filename |
| 48 | + end |
| 49 | + |
| 50 | + # Figure out the checksum for a given segment/filename based on finding |
| 51 | + # the most specific version of the file available. |
| 52 | + def get_preferred_checksum(node, cookbook_name, segment, filename) |
| 53 | + platform, version = Chef::Platform.find_platform_and_version(node) |
| 54 | + fqdn = node[:fqdn] |
| 55 | + |
| 56 | + # Most specific to least specific places to find the filename |
| 57 | + preferences = [ |
| 58 | + File.join("host-#{fqdn}", filename), |
| 59 | + File.join("#{platform}-#{version}", filename), |
| 60 | + File.join(platform, filename), |
| 61 | + File.join("default", filename) |
| 62 | + ] |
| 63 | + |
| 64 | + cookbook = get_cookbook(cookbook_name) |
| 65 | + |
| 66 | + cookbook_checksum_by_filename = Hash.new |
| 67 | + [ :resources, :providers, :recipes, :definitions, :libraries, :attributes, :files, :templates, :root_files ].each do |segment| |
| 68 | + cookbook[segment].inject(cookbook_checksum_by_filename) do |memo, segment_file| |
| 69 | + checksum = segment_file['checksum'] |
| 70 | + memo[segment_file.path] = checksum |
| 71 | + memo |
| 72 | + end |
| 73 | + end |
| 74 | + |
| 75 | + # Walk most- to least-specific until we find the filename. |
| 76 | + preferences.find { |filename| cookbook_checksum_by_filename[filename] } |
| 77 | + end |
| 78 | + |
| 79 | + # Get the cookbook from the cache or fetch if it's not yet in the |
| 80 | + # cache. |
| 81 | + def get_cookbook(cookbook_name) |
| 82 | + unless @cookbook_cache[cookbook_name] |
| 83 | + url = "/cookbooks/#{cookbook_name}" |
| 84 | + @cookbook_cache[cookbook_name] = rest.get_rest(url) |
| 85 | + end |
| 86 | + |
| 87 | + @cookbook_cache[cookbook_name] |
23 | 88 | end |
| 89 | + |
24 | 90 | end |
25 | 91 | end |
0 commit comments