/lib/chef/provider/remote_directory.rb

https://bitbucket.org/destructuring/chef · Ruby · 180 lines · 129 code · 22 blank · 29 comment · 23 complexity · 8c4e05835646af55d13d6618361f20c5 MD5 · raw file

  1. #
  2. # Author:: Adam Jacob (<adam@opscode.com>)
  3. # Copyright:: Copyright (c) 2008 Opscode, Inc.
  4. # License:: Apache License, Version 2.0
  5. #
  6. # Licensed under the Apache License, Version 2.0 (the "License");
  7. # you may not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. # http://www.apache.org/licenses/LICENSE-2.0
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. #
  18. require 'chef/provider/file'
  19. require 'chef/provider/directory'
  20. require 'chef/resource/directory'
  21. require 'chef/resource/remote_file'
  22. require 'chef/mixin/file_class'
  23. require 'chef/platform'
  24. require 'uri'
  25. require 'tempfile'
  26. require 'net/https'
  27. require 'set'
  28. class Chef
  29. class Provider
  30. class RemoteDirectory < Chef::Provider::Directory
  31. include Chef::Mixin::EnforceOwnershipAndPermissions
  32. include Chef::Mixin::FileClass
  33. def action_create
  34. super
  35. files_to_purge = Set.new(Dir.glob(::File.join(@new_resource.path, '**', '*'),
  36. ::File::FNM_DOTMATCH).select do |name|
  37. name !~ /(?:^|#{Regexp.escape(::File::SEPARATOR)})\.\.?$/
  38. end)
  39. files_to_transfer.each do |cookbook_file_relative_path|
  40. create_cookbook_file(cookbook_file_relative_path)
  41. # the file is removed from the purge list
  42. files_to_purge.delete(::File.join(@new_resource.path, cookbook_file_relative_path))
  43. # parent directories are also removed from the purge list
  44. directories=::File.dirname(::File.join(@new_resource.path, cookbook_file_relative_path)).split(::File::SEPARATOR)
  45. for i in 0..directories.length-1
  46. files_to_purge.delete(::File.join(directories[0..i]))
  47. end
  48. end
  49. purge_unmanaged_files(files_to_purge)
  50. end
  51. def action_create_if_missing
  52. # if this action is called, ignore the existing overwrite flag
  53. @new_resource.overwrite(false)
  54. action_create
  55. end
  56. protected
  57. def purge_unmanaged_files(unmanaged_files)
  58. if @new_resource.purge
  59. unmanaged_files.sort.reverse.each do |f|
  60. # file_class comes from Chef::Mixin::FileClass
  61. if ::File.directory?(f) && !Chef::Platform.windows? && !file_class.symlink?(f.dup)
  62. # Linux treats directory symlinks as files
  63. # Remove a directory as a directory when not on windows if it is not a symlink
  64. purge_directory(f)
  65. elsif ::File.directory?(f) && Chef::Platform.windows?
  66. # Windows treats directory symlinks as directories so we delete them here
  67. purge_directory(f)
  68. else
  69. converge_by("delete unmanaged file #{f}") do
  70. ::File.delete(f)
  71. Chef::Log.debug("#{@new_resource} deleted file #{f}")
  72. end
  73. end
  74. end
  75. end
  76. end
  77. def purge_directory(dir)
  78. converge_by("delete unmanaged directory #{dir}") do
  79. Dir::rmdir(dir)
  80. Chef::Log.debug("#{@new_resource} removed directory #{dir}")
  81. end
  82. end
  83. def files_to_transfer
  84. cookbook = run_context.cookbook_collection[resource_cookbook]
  85. files = cookbook.relative_filenames_in_preferred_directory(node, :files, @new_resource.source)
  86. files.sort.reverse
  87. end
  88. def directory_root_in_cookbook_cache
  89. @directory_root_in_cookbook_cache ||= begin
  90. cookbook = run_context.cookbook_collection[resource_cookbook]
  91. cookbook.preferred_filename_on_disk_location(node, :files, @new_resource.source, @new_resource.path)
  92. end
  93. end
  94. # Determine the cookbook to get the file from. If new resource sets an
  95. # explicit cookbook, use it, otherwise fall back to the implicit cookbook
  96. # i.e., the cookbook the resource was declared in.
  97. def resource_cookbook
  98. @new_resource.cookbook || @new_resource.cookbook_name
  99. end
  100. def create_cookbook_file(cookbook_file_relative_path)
  101. full_path = ::File.join(@new_resource.path, cookbook_file_relative_path)
  102. ensure_directory_exists(::File.dirname(full_path))
  103. file_to_fetch = cookbook_file_resource(full_path, cookbook_file_relative_path)
  104. if @new_resource.overwrite
  105. file_to_fetch.run_action(:create)
  106. else
  107. file_to_fetch.run_action(:create_if_missing)
  108. end
  109. @new_resource.updated_by_last_action(true) if file_to_fetch.updated?
  110. end
  111. def cookbook_file_resource(target_path, relative_source_path)
  112. cookbook_file = Chef::Resource::CookbookFile.new(target_path, run_context)
  113. cookbook_file.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
  114. cookbook_file.source(::File.join(@new_resource.source, relative_source_path))
  115. if Chef::Platform.windows? && @new_resource.files_rights
  116. @new_resource.files_rights.each_pair do |permission, *args|
  117. cookbook_file.rights(permission, *args)
  118. end
  119. end
  120. cookbook_file.mode(@new_resource.files_mode) if @new_resource.files_mode
  121. cookbook_file.group(@new_resource.files_group) if @new_resource.files_group
  122. cookbook_file.owner(@new_resource.files_owner) if @new_resource.files_owner
  123. cookbook_file.backup(@new_resource.files_backup) if @new_resource.files_backup
  124. cookbook_file
  125. end
  126. def ensure_directory_exists(path)
  127. unless ::File.directory?(path)
  128. directory_to_create = resource_for_directory(path)
  129. directory_to_create.run_action(:create)
  130. @new_resource.updated_by_last_action(true) if directory_to_create.updated?
  131. end
  132. end
  133. def resource_for_directory(path)
  134. dir = Chef::Resource::Directory.new(path, run_context)
  135. dir.cookbook_name = @new_resource.cookbook || @new_resource.cookbook_name
  136. if Chef::Platform.windows? && @new_resource.rights
  137. # rights are only meant to be applied to the toppest-level directory;
  138. # Windows will handle inheritance.
  139. if path == @new_resource.path
  140. @new_resource.rights.each do |rights| #rights is a hash
  141. permissions = rights.delete(:permissions) #delete will return the value or nil if not found
  142. principals = rights.delete(:principals)
  143. dir.rights(permissions, principals, rights)
  144. end
  145. end
  146. end
  147. dir.mode(@new_resource.mode) if @new_resource.mode
  148. dir.group(@new_resource.group)
  149. dir.owner(@new_resource.owner)
  150. dir.recursive(true)
  151. dir
  152. end
  153. def whyrun_supported?
  154. true
  155. end
  156. end
  157. end
  158. end