Class: Chef::Provider::Link

Inherits:
Chef::Provider show all
Includes:
Mixin::EnforceOwnershipAndPermissions, Mixin::FileClass
Defined in:
lib/chef/provider/link.rb

Instance Attribute Summary

Attributes inherited from Chef::Provider

#action, #cookbook_name, #current_resource, #new_resource, #recipe_name, #run_context

Instance Method Summary collapse

Methods included from Mixin::FileClass

#file_class

Methods included from Mixin::EnforceOwnershipAndPermissions

#access_controls, #enforce_ownership_and_permissions

Methods inherited from Chef::Provider

action, #action_nothing, #check_resource_semantics!, #cleanup_after_converge, #compile_and_converge_action, #converge_by, #converge_if_changed, #events, include_resource_dsl?, include_resource_dsl_module, #initialize, #node, #process_resource_requirements, provides, provides?, #requirements, #resource_collection, #resource_updated?, #run_action, #set_updated_status, supports?, use_inline_resources, #whyrun_mode?, #whyrun_supported?

Methods included from Mixin::Provides

#provided_as, #provides, #provides?

Methods included from Mixin::DescendantsTracker

#descendants, descendants, #direct_descendants, direct_descendants, #find_descendants_by_name, find_descendants_by_name, #inherited, store_inherited

Methods included from Mixin::LazyModuleInclude

#descendants, #include, #included

Methods included from Mixin::NotifyingBlock

#notifying_block, #subcontext_block

Methods included from DSL::DeclareResource

#build_resource, #declare_resource, #delete_resource, #delete_resource!, #edit_resource, #edit_resource!, #find_resource, #find_resource!, #with_run_context

Methods included from Mixin::ShellOut

#a_to_s, #clean_array, #shell_out, #shell_out!, #shell_out_compact, #shell_out_compact!, #shell_out_compact_timeout, #shell_out_compact_timeout!, #shell_out_with_systems_locale, #shell_out_with_systems_locale!

Methods included from Mixin::PathSanity

#enforce_path_sanity, #sanitized_path

Methods included from Mixin::PowershellOut

#powershell_out, #powershell_out!

Methods included from Mixin::WindowsArchitectureHelper

#assert_valid_windows_architecture!, #disable_wow64_file_redirection, #forced_32bit_override_required?, #is_i386_process_on_x86_64_windows?, #node_supports_windows_architecture?, #node_windows_architecture, #restore_wow64_file_redirection, #valid_windows_architecture?, #with_os_architecture, #wow64_architecture_override_required?, #wow64_directory

Methods included from DSL::PlatformIntrospection

#docker?, #platform?, #platform_family?, #value_for_platform, #value_for_platform_family

Constructor Details

This class inherits a constructor from Chef::Provider

Instance Method Details

#action_createObject


88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/chef/provider/link.rb', line 88

def action_create
  # current_resource is the symlink that currently exists
  # new_resource is the symlink we need to create
  #   to - the location to link to
  #   target_file - the name of the link

  if current_resource.to != canonicalize(new_resource.to) ||
      current_resource.link_type != new_resource.link_type
    # Handle the case where the symlink already exists and is pointing at a valid to_file
    if current_resource.to
      # On Windows, to fix a symlink already pointing at a directory we must first
      # ::Dir.unlink the symlink (not the directory), while if we have a symlink
      # pointing at file we must use ::File.unlink on the symlink.
      # However if the new symlink will point to a file and the current symlink is pointing at a
      # directory we want to throw an exception and calling ::File.unlink on the directory symlink
      # will throw the correct ones.
      if Chef::Platform.windows? && ::File.directory?(new_resource.to) &&
          ::File.directory?(current_resource.target_file)
        converge_by("unlink existing windows symlink to dir at #{new_resource.target_file}") do
          ::Dir.unlink(new_resource.target_file)
        end
      else
        converge_by("unlink existing symlink to file at #{new_resource.target_file}") do
          ::File.unlink(new_resource.target_file)
        end
      end
    end
    if new_resource.link_type == :symbolic
      converge_by("create symlink at #{new_resource.target_file} to #{new_resource.to}") do
        file_class.symlink(canonicalize(new_resource.to), new_resource.target_file)
        Chef::Log.debug("#{new_resource} created #{new_resource.link_type} link from #{new_resource.target_file} -> #{new_resource.to}")
        Chef::Log.info("#{new_resource} created")
        # file_class.symlink will create the link with default access controls.
        # This means that the access controls of the file could be different
        # than those captured during the initial evaluation of current_resource.
        # We need to re-evaluate the current_resource to ensure that the desired
        # access controls are applied.
        ScanAccessControl.new(new_resource, current_resource).set_all!
      end
    elsif new_resource.link_type == :hard
      converge_by("create hard link at #{new_resource.target_file} to #{new_resource.to}") do
        file_class.link(new_resource.to, new_resource.target_file)
        Chef::Log.debug("#{new_resource} created #{new_resource.link_type} link from #{new_resource.target_file} -> #{new_resource.to}")
        Chef::Log.info("#{new_resource} created")
      end
    end
  end
  if new_resource.link_type == :symbolic
    if access_controls.requires_changes?
      converge_by(access_controls.describe_changes) do
        access_controls.set_all
      end
    end
  end
end

#action_deleteObject


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/chef/provider/link.rb', line 144

def action_delete
  if current_resource.to # Exists
    if Chef::Platform.windows? && ::File.directory?(current_resource.target_file)
      converge_by("delete link to dir at #{new_resource.target_file}") do
        ::Dir.delete(new_resource.target_file)
        Chef::Log.info("#{new_resource} deleted")
      end
    else
      converge_by("delete link to file at #{new_resource.target_file}") do
        ::File.delete(new_resource.target_file)
        Chef::Log.info("#{new_resource} deleted")
      end
    end
  end
end

#canonicalize(path) ⇒ Object


84
85
86
# File 'lib/chef/provider/link.rb', line 84

def canonicalize(path)
  Chef::Platform.windows? ? path.tr("/", '\\') : path
end

#define_resource_requirementsObject


69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/chef/provider/link.rb', line 69

def define_resource_requirements
  requirements.assert(:delete) do |a|
    a.assertion do
      if current_resource.to
        current_resource.link_type == new_resource.link_type &&
          (current_resource.link_type == :symbolic || current_resource.to != "")
      else
        true
      end
    end
    a.failure_message Chef::Exceptions::Link, "Cannot delete #{new_resource} at #{new_resource.target_file}! Not a #{new_resource.link_type} link."
    a.whyrun("Would assume the link at #{new_resource.target_file} was previously created")
  end
end

#load_current_resourceObject


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/chef/provider/link.rb', line 45

def load_current_resource
  @current_resource = Chef::Resource::Link.new(new_resource.name)
  current_resource.target_file(new_resource.target_file)
  if file_class.symlink?(current_resource.target_file)
    current_resource.link_type(:symbolic)
    current_resource.to(
      canonicalize(file_class.readlink(current_resource.target_file))
    )
  else
    current_resource.link_type(:hard)
    if ::File.exists?(current_resource.target_file)
      if ::File.exists?(new_resource.to) &&
          file_class.stat(current_resource.target_file).ino ==
              file_class.stat(new_resource.to).ino
        current_resource.to(canonicalize(new_resource.to))
      else
        current_resource.to("")
      end
    end
  end
  ScanAccessControl.new(new_resource, current_resource).set_all!
  current_resource
end

Implementation components should not follow symlinks when managing access control (e.g., use lchmod instead of chmod) if the resource is a symlink.

Returns:

  • (Boolean)

163
164
165
# File 'lib/chef/provider/link.rb', line 163

def manage_symlink_access?
  new_resource.link_type == :symbolic
end