diff options
author | danielsdeleo <dan@opscode.com> | 2013-06-24 17:30:27 -0700 |
---|---|---|
committer | danielsdeleo <dan@opscode.com> | 2013-06-26 10:47:47 -0700 |
commit | 34dd4d4730656141889dc0cf9eee45e979b0a141 (patch) | |
tree | 6bcf1bc2e4efe24e2709aea01791f3d39632e91a /spec/support | |
parent | 283ab69016cb7e485ffaee0836ca486144d4de94 (diff) | |
download | chef-34dd4d4730656141889dc0cf9eee45e979b0a141.tar.gz |
Allow file resources to manage files via symlink
Fixes CHEF-4312 http://tickets.opscode.com/browse/CHEF-4312
Adds resource attribute `manage_symlink_source` to file resource and
descendents. When true, file resources will manage the source file
when a symlink exists at the destination path. When nil (default), the
source file is managed, but a warning is emitted. When false, symlinks
are not followed. In Chef 12, the default should be changed to false.
Diffstat (limited to 'spec/support')
-rw-r--r-- | spec/support/shared/functional/file_resource.rb | 190 |
1 files changed, 164 insertions, 26 deletions
diff --git a/spec/support/shared/functional/file_resource.rb b/spec/support/shared/functional/file_resource.rb index ec0ddafb00..2b4330b539 100644 --- a/spec/support/shared/functional/file_resource.rb +++ b/spec/support/shared/functional/file_resource.rb @@ -369,45 +369,183 @@ shared_examples_for "a configured file resource" do File.join(CHEF_SPEC_DATA, "file-test-target") } - before do - FileUtils.touch(symlink_target) - end - after do - FileUtils.rm_rf(symlink_target) - end + describe "when configured not to manage symlink's target" do + before(:each) do + # configure not to manage symlink source + resource.manage_symlink_source(false) - before(:each) do - if windows? - Chef::ReservedNames::Win32::File.symlink(symlink_target, path) - else - File.symlink(symlink_target, path) + # create symlinks for test context + FileUtils.touch(symlink_target) + + if windows? + Chef::ReservedNames::Win32::File.symlink(symlink_target, path) + else + File.symlink(symlink_target, path) + end end - end - after(:each) do - FileUtils.rm_rf(path) - end + after(:each) do + FileUtils.rm_rf(symlink_target) + FileUtils.rm_rf(path) + end - describe "when symlink target has correct content" do - before(:each) do - File.open(symlink_target, "wb") { |f| f.print expected_content } + describe "when symlink target has correct content" do + before(:each) do + File.open(symlink_target, "wb") { |f| f.print expected_content } + end + + it_behaves_like "file resource not pointing to a real file" end - it_behaves_like "file resource not pointing to a real file" + describe "when symlink target has the wrong content" do + before(:each) do + File.open(symlink_target, "wb") { |f| f.print "This is so wrong!!!" } + end + + after(:each) do + # symlink should never be followed + binread(symlink_target).should == "This is so wrong!!!" + end + + it_behaves_like "file resource not pointing to a real file" + end end - describe "when symlink target has the wrong content" do - before(:each) do - File.open(symlink_target, "wb") { |f| f.print "This is so wrong!!!" } + # Unix-only for now. Windows behavior may differ because of how ACL + # management handles symlinks. Since symlinks are rare on Windows and this + # feature primarily exists to support the case where a well-known file + # (e.g., resolv.conf) has been converted to a symlink, we're okay with the + # discrepancy. + context "when configured to manage the symlink source", :unix_only do + + before do + resource.manage_symlink_source(true) end - after(:each) do - # symlink should never be followed - binread(symlink_target).should == "This is so wrong!!!" + context "but the symlink is part of a loop" do + let(:link1_path) { File.join(CHEF_SPEC_DATA, "points-to-link2") } + let(:link2_path) { File.join(CHEF_SPEC_DATA, "points-to-link1") } + + before do + # point resource at link1: + resource.path(link1_path) + # create symlinks for test context + File.symlink(link1_path, link2_path) + File.symlink(link2_path, link1_path) + end + + after(:each) do + FileUtils.rm_rf(link1_path) + FileUtils.rm_rf(link2_path) + end + + it "raises an InvalidSymlink error" do + lambda { resource.run_action(:create) }.should raise_error(Chef::Exceptions::InvalidSymlink) + end + + it "issues a warning/assumption in whyrun mode" do + begin + Chef::Config[:why_run] = true + resource.run_action(:create) # should not raise + ensure + Chef::Config[:why_run] = false + end + end + end + + context "but the symlink points to a nonexistent file" do + let(:link_path) { File.join(CHEF_SPEC_DATA, "points-to-nothing") } + let(:not_existent_source) { File.join(CHEF_SPEC_DATA, "i-am-not-here") } + + before do + resource.path(link_path) + # create symlinks for test context + File.symlink(not_existent_source, link_path) + FileUtils.rm_rf(not_existent_source) + end + + after(:each) do + FileUtils.rm_rf(link_path) + end + it "raises an InvalidSymlink error" do + lambda { resource.run_action(:create) }.should raise_error(Chef::Exceptions::InvalidSymlink) + end + + it "issues a warning/assumption in whyrun mode" do + begin + Chef::Config[:why_run] = true + resource.run_action(:create) # should not raise + ensure + Chef::Config[:why_run] = false + end + end + end + + context "but the symlink is points to a non-file fs entry" do + let(:link_path) { File.join(CHEF_SPEC_DATA, "points-to-dir") } + let(:not_a_file_path) { File.join(CHEF_SPEC_DATA, "dir-at-end-of-symlink") } + + before do + # point resource at link1: + resource.path(link_path) + # create symlinks for test context + File.symlink(not_a_file_path, link_path) + Dir.mkdir(not_a_file_path) + end + + after(:each) do + FileUtils.rm_rf(link_path) + FileUtils.rm_rf(not_a_file_path) + end + + it "raises an InvalidSymlink error" do + lambda { resource.run_action(:create) }.should raise_error(Chef::Exceptions::FileTypeMismatch) + end + + it "issues a warning/assumption in whyrun mode" do + begin + Chef::Config[:why_run] = true + resource.run_action(:create) # should not raise + ensure + Chef::Config[:why_run] = false + end + end + end + + context "when the symlink source is a real file" do + + let(:wrong_content) { "this is the wrong content" } + let(:link_path) { File.join(CHEF_SPEC_DATA, "points-to-real-file") } + + before do + # point resource at link: + resource.path(link_path) + # create symlinks for test context + File.symlink(path, link_path) + + # Create source (real) file + File.open(path, "wb") { |f| f.write(wrong_content) } + end + + include_context "setup broken permissions" + + include_examples "a securable resource with existing target" + + after(:each) do + # shared examples should not change our test setup of a file resource + # pointing at a symlink: + resource.path.should == link_path + FileUtils.rm_rf(link_path) + end + + it "does not replace the symlink with a real file" do + resource.run_action(:create) + File.should be_symlink(link_path) + end + end - it_behaves_like "file resource not pointing to a real file" end end |