# File lib/chef/provider/file.rb, line 244 def action_create if !::File.exists?(@new_resource.path) description = [] desc = "create new file #{@new_resource.path}" desc << " with content checksum #{short_cksum(new_resource_content_checksum)}" if new_resource.content description << desc description << diff_current_from_content(@new_resource.content) converge_by(description) do Chef::Log.info("entered create") ::File.open(@new_resource.path, "w+") {|f| f.write @new_resource.content } access_controls.set_all Chef::Log.info("#{@new_resource} created file #{@new_resource.path}") update_new_file_state end else set_content unless @new_resource.content.nil? set_all_access_controls end end
# File lib/chef/provider/file.rb, line 275 def action_create_if_missing if ::File.exists?(@new_resource.path) Chef::Log.debug("#{@new_resource} exists at #{@new_resource.path} taking no action.") else action_create end end
# File lib/chef/provider/file.rb, line 283 def action_delete if ::File.exists?(@new_resource.path) converge_by("delete file #{@new_resource.path}") do backup unless ::File.symlink?(@new_resource.path) ::File.delete(@new_resource.path) Chef::Log.info("#{@new_resource} deleted file at #{@new_resource.path}") end end end
# File lib/chef/provider/file.rb, line 293 def action_touch action_create converge_by("update utime on file #{@new_resource.path}") do time = Time.now ::File.utime(time, time, @new_resource.path) Chef::Log.info("#{@new_resource} updated atime and mtime to #{time}") end end
# File lib/chef/provider/file.rb, line 302 def backup(file=nil) file ||= @new_resource.path if @new_resource.backup != false && @new_resource.backup > 0 && ::File.exist?(file) time = Time.now savetime = time.strftime("%Y%m%d%H%M%S") backup_filename = "#{@new_resource.path}.chef-#{savetime}" backup_filename = backup_filename.sub(/^([A-Za-z]:)/, "") #strip drive letter on Windows # if :file_backup_path is nil, we fallback to the old behavior of # keeping the backup in the same directory. We also need to to_s it # so we don't get a type error around implicit to_str conversions. prefix = Chef::Config[:file_backup_path].to_s backup_path = ::File.join(prefix, backup_filename) FileUtils.mkdir_p(::File.dirname(backup_path)) if Chef::Config[:file_backup_path] FileUtils.cp(file, backup_path, :preserve => true) Chef::Log.info("#{@new_resource} backed up to #{backup_path}") # Clean up after the number of backups slice_number = @new_resource.backup backup_files = Dir[::File.join(prefix, ".#{@new_resource.path}.chef-*")].sort { |a,b| b <=> a } if backup_files.length >= @new_resource.backup remainder = backup_files.slice(slice_number..-1) remainder.each do |backup_to_delete| FileUtils.rm(backup_to_delete) Chef::Log.info("#{@new_resource} removed backup at #{backup_to_delete}") end end end end
Compare the content of a file. Returns true if they are the same, false if they are not.
# File lib/chef/provider/file.rb, line 205 def compare_content checksum(@current_resource.path) == new_resource_content_checksum end
# File lib/chef/provider/file.rb, line 177 def define_resource_requirements # this must be evaluated before whyrun messages are printed access_controls.requires_changes? requirements.assert(:create, :create_if_missing, :touch) do |a| # Make sure the parent dir exists, or else fail. # for why run, print a message explaining the potential error. parent_directory = ::File.dirname(@new_resource.path) a.assertion { ::File.directory?(parent_directory) } a.failure_message(Chef::Exceptions::EnclosingDirectoryDoesNotExist, "Parent directory #{parent_directory} does not exist.") a.whyrun("Assuming directory #{parent_directory} would have been created") end # Make sure the file is deletable if it exists. Otherwise, fail. requirements.assert(:delete) do |a| a.assertion do if ::File.exists?(@new_resource.path) ::File.writable?(@new_resource.path) else true end end a.failure_message(Chef::Exceptions::InsufficientPermissions,"File #{@new_resource.path} exists but is not writable so it cannot be deleted") end end
# File lib/chef/provider/file.rb, line 331 def deploy_tempfile Tempfile.open(::File.basename(@new_resource.name)) do |tempfile| yield tempfile temp_res = Chef::Resource::CookbookFile.new(@new_resource.name) temp_res.path(tempfile.path) ac = Chef::FileAccessControl.new(temp_res, @new_resource, self) ac.set_all! FileUtils.mv(tempfile.path, @new_resource.path) end end
# File lib/chef/provider/file.rb, line 69 def diff_current(temp_path) suppress_resource_reporting = false return [ "(diff output suppressed by config)" ] if Chef::Config[:diff_disabled] return [ "(no temp file with new content, diff output suppressed)" ] unless ::File.exists?(temp_path) # should never happen? # solaris does not support diff -N, so create tempfile to diff against if we are creating a new file target_path = if ::File.exists?(@current_resource.path) @current_resource.path else suppress_resource_reporting = true # suppress big diffs going to resource reporting service tempfile = Tempfile.new('chef-tempfile') tempfile.path end diff_filesize_threshold = Chef::Config[:diff_filesize_threshold] diff_output_threshold = Chef::Config[:diff_output_threshold] if ::File.size(target_path) > diff_filesize_threshold || ::File.size(temp_path) > diff_filesize_threshold return [ "(file sizes exceed #{diff_filesize_threshold} bytes, diff output suppressed)" ] end # MacOSX(BSD?) diff will *sometimes* happily spit out nasty binary diffs return [ "(current file is binary, diff output suppressed)"] if is_binary?(target_path) return [ "(new content is binary, diff output suppressed)"] if is_binary?(temp_path) begin # -u: Unified diff format result = shell_out("diff -u #{target_path} #{temp_path}" ) rescue Exception => e # Should *not* receive this, but in some circumstances it seems that # an exception can be thrown even using shell_out instead of shell_out! return [ "Could not determine diff. Error: #{e.message}" ] end # diff will set a non-zero return code even when there's # valid stdout results, if it encounters something unexpected # So as long as we have output, we'll show it. if not result.stdout.empty? if result.stdout.length > diff_output_threshold [ "(long diff of over #{diff_output_threshold} characters, diff output suppressed)" ] else val = result.stdout.split("\n") val.delete("\\ No newline at end of file") @new_resource.diff(val.join("\\n")) unless suppress_resource_reporting val end elsif not result.stderr.empty? [ "Could not determine diff. Error: #{result.stderr}" ] else [ "(no diff)" ] end end
# File lib/chef/provider/file.rb, line 49 def diff_current_from_content(new_content) result = nil Tempfile.open("chef-diff") do |file| file.write new_content file.close result = diff_current file.path end result end
# File lib/chef/provider/file.rb, line 59 def is_binary?(path) ::File.open(path) do |file| buff = file.read(Chef::Config[:diff_filesize_threshold]) buff = "" if buff.nil? return buff !~ /^[\r[:print:]]*$/ end end
# File lib/chef/provider/file.rb, line 127 def load_current_resource # Every child should be specifying their own constructor, so this # should only be run in the file case. @current_resource ||= Chef::Resource::File.new(@new_resource.name) @new_resource.path.gsub!(/\\/, "/") # for Windows @current_resource.path(@new_resource.path) if !::File.directory?(@new_resource.path) if ::File.exist?(@new_resource.path) if @action != :create_if_missing @current_resource.checksum(checksum(@new_resource.path)) end end end load_current_resource_attrs setup_acl @current_resource end
# File lib/chef/provider/file.rb, line 146 def load_current_resource_attrs if Chef::Platform.windows? # TODO: To work around CHEF-3554, add support for Windows # equivalent, or implicit resource reporting won't work for # Windows. return end if ::File.exist?(@new_resource.path) stat = ::File.stat(@new_resource.path) @current_resource.owner(stat.uid) @current_resource.mode(stat.mode & 07777) @current_resource.group(stat.gid) if @new_resource.group.nil? @new_resource.group(@current_resource.group) end if @new_resource.owner.nil? @new_resource.owner(@current_resource.owner) end if @new_resource.mode.nil? @new_resource.mode(@current_resource.mode) end end end
# File lib/chef/provider/file.rb, line 265 def set_all_access_controls if access_controls.requires_changes? converge_by(access_controls.describe_changes) do access_controls.set_all #Update file state with new access values update_new_file_state end end end
Set the content of the file, assuming it is not set correctly already.
# File lib/chef/provider/file.rb, line 210 def set_content unless compare_content description = [] description << "update content in file #{@new_resource.path} from #{short_cksum(@current_resource.checksum)} to #{short_cksum(new_resource_content_checksum)}" description << diff_current_from_content(@new_resource.content) converge_by(description) do backup @new_resource.path if ::File.exists?(@new_resource.path) ::File.open(@new_resource.path, "w") {|f| f.write @new_resource.content } Chef::Log.info("#{@new_resource} contents updated") end end end
# File lib/chef/provider/file.rb, line 172 def setup_acl @acl_scanner = ScanAccessControl.new(@new_resource, @current_resource) @acl_scanner.set_all! end
if you are using a tempfile before creating, you must override the default with the tempfile, since the file at @new_resource.path will not be updated on converge
# File lib/chef/provider/file.rb, line 226 def update_new_file_state(path=@new_resource.path) if !::File.directory?(path) @new_resource.checksum(checksum(path)) end if Chef::Platform.windows? # TODO: To work around CHEF-3554, add support for Windows # equivalent, or implicit resource reporting won't work for # Windows. return end stat = ::File.stat(path) @new_resource.owner(stat.uid) @new_resource.mode(stat.mode & 07777) @new_resource.group(stat.gid) end
Generated with the Darkfish Rdoc Generator 2.