diff options
Diffstat (limited to 'lib/chef/cookbook')
-rw-r--r-- | lib/chef/cookbook/file_system_file_vendor.rb | 3 | ||||
-rw-r--r-- | lib/chef/cookbook/file_vendor.rb | 32 | ||||
-rw-r--r-- | lib/chef/cookbook/remote_file_vendor.rb | 3 | ||||
-rw-r--r-- | lib/chef/cookbook/synchronizer.rb | 58 |
4 files changed, 84 insertions, 12 deletions
diff --git a/lib/chef/cookbook/file_system_file_vendor.rb b/lib/chef/cookbook/file_system_file_vendor.rb index 8896e3ed30..e351ec4702 100644 --- a/lib/chef/cookbook/file_system_file_vendor.rb +++ b/lib/chef/cookbook/file_system_file_vendor.rb @@ -31,6 +31,9 @@ class Chef # non-sensical. class FileSystemFileVendor < FileVendor + attr_reader :cookbook_name + attr_reader :repo_paths + def initialize(manifest, *repo_paths) @cookbook_name = manifest[:cookbook_name] @repo_paths = repo_paths.flatten diff --git a/lib/chef/cookbook/file_vendor.rb b/lib/chef/cookbook/file_vendor.rb index 406f23ca25..b82b52f90c 100644 --- a/lib/chef/cookbook/file_vendor.rb +++ b/lib/chef/cookbook/file_vendor.rb @@ -24,15 +24,39 @@ class Chef # This class handles fetching of cookbook files based on specificity. class FileVendor - def self.on_create(&block) - @instance_creator = block + @vendor_class = nil + @initialization_options = nil + + # Configures FileVendor to use the RemoteFileVendor implementation. After + # calling this, subsequent calls to create_from_manifest will return a + # RemoteFileVendor object initialized with the given http_client + def self.fetch_from_remote(http_client) + @vendor_class = RemoteFileVendor + @initialization_options = http_client + end + + def self.fetch_from_disk(cookbook_paths) + @vendor_class = FileSystemFileVendor + @initialization_options = cookbook_paths + end + + # Returns the implementation class that is currently configured, or `nil` + # if one has not been configured yet. + def self.vendor_class + @vendor_class + end + + def self.initialization_options + @initialization_options end # Factory method that creates the appropriate kind of # Cookbook::FileVendor to serve the contents of the manifest def self.create_from_manifest(manifest) - raise "Must call Chef::Cookbook::FileVendor.on_create before calling create_from_manifest factory" unless defined?(@instance_creator) - @instance_creator.call(manifest) + if @vendor_class.nil? + raise "Must configure FileVendor to use a specific implementation before creating an instance" + end + @vendor_class.new(manifest, @initialization_options) end # Gets the on-disk location for the given cookbook file. diff --git a/lib/chef/cookbook/remote_file_vendor.rb b/lib/chef/cookbook/remote_file_vendor.rb index 49de62cf65..2ddce31001 100644 --- a/lib/chef/cookbook/remote_file_vendor.rb +++ b/lib/chef/cookbook/remote_file_vendor.rb @@ -25,6 +25,9 @@ class Chef # if not available, loading them from the remote server. class RemoteFileVendor < FileVendor + attr_reader :rest + attr_reader :cookbook_name + def initialize(manifest, rest) @manifest = manifest @cookbook_name = @manifest[:cookbook_name] diff --git a/lib/chef/cookbook/synchronizer.rb b/lib/chef/cookbook/synchronizer.rb index 7df3f554bb..1b96d0510b 100644 --- a/lib/chef/cookbook/synchronizer.rb +++ b/lib/chef/cookbook/synchronizer.rb @@ -10,6 +10,8 @@ class Chef # cache. class CookbookCacheCleaner + attr_accessor :skip_removal + # Setup a notification to clear the valid_cache_entries when a Chef client # run starts Chef::Client.when_run_starts do |run_status| @@ -40,15 +42,17 @@ class Chef end def cleanup_file_cache - unless Chef::Config[:solo] + unless Chef::Config[:solo] || skip_removal # Delete each file in the cache that we didn't encounter in the # manifest. - cache.find(File.join(%w{cookbooks ** *})).each do |cache_filename| + cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_filename| unless @valid_cache_entries[cache_filename] Chef::Log.info("Removing #{cache_filename} from the cache; it is no longer needed by chef-client.") cache.delete(cache_filename) end end + else + Chef::Log.info("Skipping removal of unused files from the cache") end end @@ -59,6 +63,8 @@ class Chef class CookbookSynchronizer CookbookFile = Struct.new(:cookbook, :segment, :manifest_record) + attr_accessor :remove_obsoleted_files + def initialize(cookbooks_by_name, events) @eager_segments = Chef::CookbookVersion::COOKBOOK_SEGMENTS.dup unless Chef::Config[:no_lazy_load] @@ -70,6 +76,7 @@ class Chef @cookbooks_by_name, @events = cookbooks_by_name, events @cookbook_full_file_paths = {} + @remove_obsoleted_files = true end def cache @@ -92,6 +99,10 @@ class Chef @cookbooks_by_name.key?(cookbook_name) end + def cookbook_segment(cookbook_name, segment) + @cookbooks_by_name[cookbook_name].manifest[segment] + end + def files @files ||= cookbooks.inject([]) do |memo, cookbook| @eager_segments.each do |segment| @@ -169,12 +180,10 @@ class Chef @cookbook_full_file_paths[file.cookbook][file.segment] << full_path end - # Iterates over cached cookbooks' files, removing files belonging to - # cookbooks that don't appear in +cookbook_hash+ - def clear_obsoleted_cookbooks - @events.cookbook_clean_start - # Remove all cookbooks no longer relevant to this node - cache.find(File.join(%w{cookbooks ** *})).each do |cache_file| + # remove cookbooks that are not referenced in the expanded run_list at all + # (if we have an override run_list we may not want to do this) + def remove_old_cookbooks + cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file| cache_file =~ /^cookbooks\/([^\/]+)\// unless have_cookbook?($1) Chef::Log.info("Removing #{cache_file} from the cache; its cookbook is no longer needed on this client.") @@ -182,6 +191,39 @@ class Chef @events.removed_cookbook_file(cache_file) end end + end + + # remove deleted files in cookbooks that are being used on the node + def remove_deleted_files + cache.find(File.join(%w{cookbooks ** {*,.*}})).each do |cache_file| + md = cache_file.match(/^cookbooks\/([^\/]+)\/([^\/]+)\/(.*)/) + next unless md + ( cookbook_name, segment, file ) = md[1..3] + if have_cookbook?(cookbook_name) + manifest_segment = cookbook_segment(cookbook_name, segment) + if manifest_segment.select { |manifest_record| manifest_record["path"] == "#{segment}/#{file}" }.empty? + Chef::Log.info("Removing #{cache_file} from the cache; its is no longer in the cookbook manifest.") + cache.delete(cache_file) + @events.removed_cookbook_file(cache_file) + end + end + end + end + + # Iterates over cached cookbooks' files, removing files belonging to + # cookbooks that don't appear in +cookbook_hash+ + def clear_obsoleted_cookbooks + @events.cookbook_clean_start + + if remove_obsoleted_files + remove_old_cookbooks + else + Chef::Log.info("Skipping removal of obsoleted cookbooks from the cache") + CookbookCacheCleaner.instance.skip_removal = true + end + + remove_deleted_files + @events.cookbook_clean_complete end |