class Chef::Provider::File

Attributes

deployment_strategy[R]

Public Class Methods

new(new_resource, run_context) click to toggle source
Calls superclass method Chef::Provider.new
# File lib/chef/provider/file.rb, line 61
def initialize(new_resource, run_context)
  @content_class ||= Chef::Provider::File::Content
  if new_resource.respond_to?(:atomic_update)
    @deployment_strategy = Chef::FileContentManagement::Deploy.strategy(new_resource.atomic_update)
  end
  super
end

Public Instance Methods

action_create() click to toggle source
# File lib/chef/provider/file.rb, line 118
def action_create
  do_unlink
  do_create_file
  do_contents_changes
  do_acl_changes
  do_selinux
  load_resource_attributes_from_file(@new_resource)
end
action_create_if_missing() click to toggle source
# File lib/chef/provider/file.rb, line 127
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
action_delete() click to toggle source
# File lib/chef/provider/file.rb, line 135
def action_delete
  if ::File.exists?(@new_resource.path)
    converge_by("delete file #{@new_resource.path}") do
      do_backup unless file_class.symlink?(@new_resource.path)
      ::File.delete(@new_resource.path)
      Chef::Log.info("#{@new_resource} deleted file at #{@new_resource.path}")
    end
  end
end
action_touch() click to toggle source
# File lib/chef/provider/file.rb, line 145
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
define_resource_requirements() click to toggle source
# File lib/chef/provider/file.rb, line 87
def define_resource_requirements
  # deep inside FAC we have to assert requirements, so call FACs hook to set that up
  access_controls.define_resource_requirements
  # Make sure the parent directory exists, otherwise fail.  For why-run assume it would have been created.
  requirements.assert(:create, :create_if_missing, :touch) do |a|
    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.
  if ::File.exists?(@new_resource.path)
    requirements.assert(:delete) do |a|
      a.assertion { ::File.writable?(@new_resource.path) }
      a.failure_message(Chef::Exceptions::InsufficientPermissions,"File #{@new_resource.path} exists but is not writable so it cannot be deleted")
    end
  end

  error, reason, whyrun_message = inspect_existing_fs_entry
  requirements.assert(:create) do |a|
    a.assertion { error.nil? }
    a.failure_message(error, reason)
    a.whyrun(whyrun_message)
    # Subsequent attempts to read the fs entry at the path (e.g., for
    # calculating checksums) could blow up, so give up trying to continue
    # why-running.
    a.block_action!
  end
end
load_current_resource() click to toggle source
# File lib/chef/provider/file.rb, line 73
def load_current_resource
  # Let children resources override constructing the @current_resource
  @current_resource ||= Chef::Resource::File.new(@new_resource.name)
  @current_resource.path(@new_resource.path)
  if ::File.exists?(@current_resource.path) && ::File.file?(::File.realpath(@current_resource.path))
    if managing_content?
      Chef::Log.debug("#{@new_resource} checksumming file at #{@new_resource.path}.")
      @current_resource.checksum(checksum(@current_resource.path))
    end
    load_resource_attributes_from_file(@current_resource)
  end
  @current_resource
end
whyrun_supported?() click to toggle source
# File lib/chef/provider/file.rb, line 69
def whyrun_supported?
  true
end

Private Instance Methods

content() click to toggle source
# File lib/chef/provider/file.rb, line 251
def content
  @content ||= begin
    load_current_resource if @current_resource.nil?
    @content_class.new(@new_resource, @current_resource, @run_context)
  end
end
contents_changed?() click to toggle source
# File lib/chef/provider/file.rb, line 405
def contents_changed?
  Chef::Log.debug "calculating checksum of #{tempfile.path} to compare with #{@current_resource.checksum}"
  checksum(tempfile.path) != @current_resource.checksum
end
diff() click to toggle source
# File lib/chef/provider/file.rb, line 335
def diff
  @diff ||= Chef::Util::Diff.new
end
do_acl_changes() click to toggle source
# File lib/chef/provider/file.rb, line 397
def do_acl_changes
  if access_controls.requires_changes?
    converge_by(access_controls.describe_changes) do
      access_controls.set_all
    end
  end
end
do_backup(file = nil) click to toggle source
# File lib/chef/provider/file.rb, line 331
def do_backup(file = nil)
  Chef::Util::Backup.new(@new_resource, file).backup!
end
do_contents_changes() click to toggle source
# File lib/chef/provider/file.rb, line 348
      def do_contents_changes
        # a nil tempfile is okay, means the resource has no content or no new content
        return if tempfile.nil?
        # but a tempfile that has no path or doesn't exist should not happen
        if tempfile.path.nil? || !::File.exists?(tempfile.path)
          raise "chef-client is confused, trying to deploy a file that has no path or does not exist..."
        end

        # the file? on the next line suppresses the case in why-run when we have a not-file here that would have otherwise been removed
        if ::File.file?(@new_resource.path) && contents_changed?
          description = [ "update content in file #{@new_resource.path} from \
#{short_cksum(@current_resource.checksum)} to #{short_cksum(checksum(tempfile.path))}" ]

          # Hide the diff output if the resource is marked as a sensitive resource
          if @new_resource.sensitive
            @new_resource.diff("suppressed sensitive resource")
            description << "suppressed sensitive resource"
          else
            diff.diff(@current_resource.path, tempfile.path)
            @new_resource.diff( diff.for_reporting ) unless file_created?
            description << diff.for_output
          end

          converge_by(description) do
            update_file_contents
          end
        end

        # unlink necessary to clean up in why-run mode
        tempfile.close
        tempfile.unlink
      end
do_create_file() click to toggle source
# File lib/chef/provider/file.rb, line 315
def do_create_file
  @file_created = false
  if !::File.exists?(@new_resource.path) || file_unlinked?
    converge_by("create new file #{@new_resource.path}") do
      deployment_strategy.create(@new_resource.path)
      Chef::Log.info("#{@new_resource} created file #{@new_resource.path}")
    end
    @file_created = true
  end
end
do_selinux(recursive = false) click to toggle source

This logic ideally will be made into some kind of generic platform-dependent post-converge hook for file-like resources, but for now we only have the single selinux use case.

# File lib/chef/provider/file.rb, line 385
def do_selinux(recursive = false)
  if resource_updated? && Chef::Config[:enable_selinux_file_permission_fixup]
    if selinux_enabled?
      converge_by("restore selinux security context") do
        restore_security_context(::File.realpath(@new_resource.path), recursive)
      end
    else
      Chef::Log.debug "selinux utilities can not be found. Skipping selinux permission fixup."
    end
  end
end
file_created?() click to toggle source

#do_contents_changes needs to know if #do_create_file created a file or not

# File lib/chef/provider/file.rb, line 327
def file_created?
  @file_created == true
end
file_type_string(path) click to toggle source
# File lib/chef/provider/file.rb, line 258
def file_type_string(path)
  case
  when ::File.blockdev?(path)
    "block device"
  when ::File.chardev?(path)
    "char device"
  when ::File.directory?(path)
    "directory"
  when ::File.pipe?(path)
    "pipe"
  when ::File.socket?(path)
    "socket"
  when file_class.symlink?(path)
    "symlink"
  else
    "unknown filetype"
  end
end
file_unlinked?() click to toggle source
# File lib/chef/provider/file.rb, line 311
def file_unlinked?
  @file_unlinked == true
end
inspect_existing_fs_entry() click to toggle source

Handles resource requirements for action :create when some fs entry already exists at the destination path. For actions other than create, we don't care what kind of thing is at the destination path because:

  • for :create_if_missing, we're assuming the user wanted to avoid blowing away the non-file here

  • for :touch, we can modify perms of whatever is at this path, regardless of its type

  • for :delete, we can blow away whatever is here, regardless of its type

For the action :create case, we need to deal with user-selectable behavior to see if we're in an error condition.

  • If there's no file at the destination path currently, we're cool to create it.

  • If the fs entry that currently exists at the destination is a regular file, we're cool to update it with new content.

  • If the fs entry is a symlink AND the resource has `manage_symlink_source` enabled, we need to verify that the symlink is a valid pointer to a real file. If it is, we can manage content and permissions on the symlink source, otherwise, error.

  • If `manage_symlink_source` is not enabled, fall through.

  • If force_unlink is true, action :create will unlink whatever is in the way.

  • If force_unlink is false, we're in an exceptional situation, so we want to error.

Note that this method returns values to be used with requirement assertions, which then decide whether or not to raise or issue a warning for whyrun mode.

# File lib/chef/provider/file.rb, line 197
def inspect_existing_fs_entry
  path = @new_resource.path

  if !l_exist?(path)
    [nil, nil, nil]
  elsif real_file?(path)
    [nil, nil, nil]
  elsif file_class.symlink?(path) && @new_resource.manage_symlink_source
    verify_symlink_sanity(path)
  elsif file_class.symlink?(@new_resource.path) && @new_resource.manage_symlink_source.nil?
    Chef::Log.warn("File #{path} managed by #{@new_resource} is really a symlink. Managing the source file instead.")
    Chef::Log.warn("Disable this warning by setting `manage_symlink_source true` on the resource")
    Chef::Log.warn("In a future Chef release, 'manage_symlink_source' will not be enabled by default")
    verify_symlink_sanity(path)
  elsif @new_resource.force_unlink
    [nil, nil, nil]
  else
    [ Chef::Exceptions::FileTypeMismatch,
      "File #{path} exists, but is a #{file_type_string(@new_resource.path)}, set force_unlink to true to remove",
      "Assuming #{file_type_string(@new_resource.path)} at #{@new_resource.path} would have been removed by a previous resource"
    ]
  end
end
l_exist?(path) click to toggle source

Similar to File.exist?, but also returns true in the case that the named file is a broken symlink.

# File lib/chef/provider/file.rb, line 283
def l_exist?(path)
  ::File.exist?(path) || file_class.symlink?(path)
end
load_resource_attributes_from_file(resource) click to toggle source
# File lib/chef/provider/file.rb, line 419
def load_resource_attributes_from_file(resource)

  if Chef::Platform.windows?
    # This is a work around for CHEF-3554.
    # OC-6534: is tracking the real fix for this workaround.
    # Add support for Windows equivalent, or implicit resource
    # reporting won't work for Windows.
    return
  end
  acl_scanner = ScanAccessControl.new(@new_resource, resource)
  acl_scanner.set_all!
end
managing_content?() click to toggle source

What to check in this resource to see if we're going to be actively managing content (for things like doing checksums in #load_current_resource). Expected to be overridden in subclasses.

# File lib/chef/provider/file.rb, line 166
def managing_content?
  return true if @new_resource.checksum
  return true if !@new_resource.content.nil? && @action != :create_if_missing
  false
end
real_file?(path) click to toggle source
# File lib/chef/provider/file.rb, line 277
def real_file?(path)
  !file_class.symlink?(path) && ::File.file?(path)
end
short_cksum(checksum) click to toggle source
# File lib/chef/provider/file.rb, line 414
def short_cksum(checksum)
  return "none" if checksum.nil?
  checksum.slice(0,6)
end
tempfile() click to toggle source
# File lib/chef/provider/file.rb, line 410
def tempfile
  content.tempfile
end
update_file_contents() click to toggle source
# File lib/chef/provider/file.rb, line 339
def update_file_contents
  do_backup unless file_created?
  deployment_strategy.deploy(tempfile.path, ::File.realpath(@new_resource.path))
  Chef::Log.info("#{@new_resource} updated file contents #{@new_resource.path}")
  if managing_content?
    @new_resource.checksum(checksum(@new_resource.path)) # for reporting
  end
end