diff options
Diffstat (limited to 'lib/chef/provider/remote_directory.rb')
-rw-r--r-- | lib/chef/provider/remote_directory.rb | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/lib/chef/provider/remote_directory.rb b/lib/chef/provider/remote_directory.rb new file mode 100644 index 0000000000..0412e8a668 --- /dev/null +++ b/lib/chef/provider/remote_directory.rb @@ -0,0 +1,180 @@ +# +# Author:: Adam Jacob (<adam@opscode.com>) +# Copyright:: Copyright (c) 2008 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/provider/file' +require 'chef/provider/directory' +require 'chef/resource/directory' +require 'chef/resource/remote_file' +require 'chef/mixin/file_class' +require 'chef/platform' +require 'uri' +require 'tempfile' +require 'net/https' +require 'set' + +class Chef + class Provider + class RemoteDirectory < Chef::Provider::Directory + + include Chef::Mixin::EnforceOwnershipAndPermissions + include Chef::Mixin::FileClass + + def action_create + super + files_to_purge = Set.new(Dir.glob(::File.join(@new_resource.path, '**', '*'), + ::File::FNM_DOTMATCH).select do |name| + name !~ /(?:^|#{Regexp.escape(::File::SEPARATOR)})\.\.?$/ + end) + + + files_to_transfer.each do |cookbook_file_relative_path| + create_cookbook_file(cookbook_file_relative_path) + # the file is removed from the purge list + files_to_purge.delete(::File.join(@new_resource.path, cookbook_file_relative_path)) + # parent directories are also removed from the purge list + directories=::File.dirname(::File.join(@new_resource.path, cookbook_file_relative_path)).split(::File::SEPARATOR) + for i in 0..directories.length-1 + files_to_purge.delete(::File.join(directories[0..i])) + end + end + purge_unmanaged_files(files_to_purge) + end + + def action_create_if_missing + # if this action is called, ignore the existing overwrite flag + @new_resource.overwrite(false) + action_create + end + + protected + + def purge_unmanaged_files(unmanaged_files) + if @new_resource.purge + unmanaged_files.sort.reverse.each do |f| + # file_class comes from Chef::Mixin::FileClass + if ::File.directory?(f) && !Chef::Platform.windows? && !file_class.symlink?(f.dup) + # Linux treats directory symlinks as files + # Remove a directory as a directory when not on windows if it is not a symlink + purge_directory(f) + elsif ::File.directory?(f) && Chef::Platform.windows? + # Windows treats directory symlinks as directories so we delete them here + purge_directory(f) + else + converge_by("delete unmanaged file #{f}") do + ::File.delete(f) + Chef::Log.debug("#{@new_resource} deleted file #{f}") + end + end + end + end + end + + def purge_directory(dir) + converge_by("delete unmanaged directory #{dir}") do + Dir::rmdir(dir) + Chef::Log.debug("#{@new_resource} removed directory #{dir}") + end + end + + def files_to_transfer + cookbook = run_context.cookbook_collection[resource_cookbook] + files = cookbook.relative_filenames_in_preferred_directory(node, :files, @new_resource.source) + files.sort.reverse + end + + def directory_root_in_cookbook_cache + @directory_root_in_cookbook_cache ||= begin + cookbook = run_context.cookbook_collection[resource_cookbook] + cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path) + end + end + + # Determine the cookbook to get the file from. If new resource sets an + # explicit cookbook, use it, otherwise fall back to the implicit cookbook + # i.e., the cookbook the resource was declared in. + def resource_cookbook + @new_resource.cookbook || @new_resource.cookbook_name + end + + def create_cookbook_file(cookbook_file_relative_path) + full_path = ::File.join(@new_resource.path, cookbook_file_relative_path) + + ensure_directory_exists(::File.dirname(full_path)) + + file_to_fetch = cookbook_file_resource(full_path, cookbook_file_relative_path) + if @new_resource.overwrite + file_to_fetch.run_action(:create) + else + file_to_fetch.run_action(:create_if_missing) + end + @new_resource.updated_by_last_action(true) if file_to_fetch.updated? + end + + def cookbook_file_resource(target_path, relative_source_path) + cookbook_file = Chef::Resource::CookbookFile.new(target_path, run_context) + cookbook_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name + cookbook_file.source(::File.join(@new_resource.source, relative_source_path)) + if Chef::Platform.windows? && @new_resource.files_rights + @new_resource.files_rights.each_pair do |permission, *args| + cookbook_file.rights(permission, *args) + end + end + cookbook_file.mode(@new_resource.files_mode) if @new_resource.files_mode + cookbook_file.group(@new_resource.files_group) if @new_resource.files_group + cookbook_file.owner(@new_resource.files_owner) if @new_resource.files_owner + cookbook_file.backup(@new_resource.files_backup) if @new_resource.files_backup + + cookbook_file + end + + def ensure_directory_exists(path) + unless ::File.directory?(path) + directory_to_create = resource_for_directory(path) + directory_to_create.run_action(:create) + @new_resource.updated_by_last_action(true) if directory_to_create.updated? + end + end + + def resource_for_directory(path) + dir = Chef::Resource::Directory.new(path, run_context) + dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name + if Chef::Platform.windows? && @new_resource.rights + # rights are only meant to be applied to the toppest-level directory; + # Windows will handle inheritance. + if path == @new_resource.path + @new_resource.rights.each do |rights| #rights is a hash + permissions = rights.delete(:permissions) #delete will return the value or nil if not found + principals = rights.delete(:principals) + dir.rights(permissions, principals, rights) + end + end + end + dir.mode(@new_resource.mode) if @new_resource.mode + dir.group(@new_resource.group) + dir.owner(@new_resource.owner) + dir.recursive(true) + dir + end + + def whyrun_supported? + true + end + + end + end +end |