summaryrefslogtreecommitdiff
path: root/lib/chef/cookbook
diff options
context:
space:
mode:
Diffstat (limited to 'lib/chef/cookbook')
-rw-r--r--lib/chef/cookbook/file_system_file_vendor.rb3
-rw-r--r--lib/chef/cookbook/file_vendor.rb32
-rw-r--r--lib/chef/cookbook/remote_file_vendor.rb3
-rw-r--r--lib/chef/cookbook/synchronizer.rb58
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