diff options
author | antima-gupta <agupta@msystechnologies.com> | 2020-08-19 18:01:37 +0530 |
---|---|---|
committer | antima-gupta <agupta@msystechnologies.com> | 2020-08-27 14:10:18 +0530 |
commit | f9a0013089ce29f6ffb853f7303be61577bece32 (patch) | |
tree | 67150ca329a49ac3b07665be573c0faa1c53b7ed | |
parent | f05dc3618241eaac5755b9904fc9ceb43e59a455 (diff) | |
download | chef-f9a0013089ce29f6ffb853f7303be61577bece32.tar.gz |
Fixed mount Resource for bind mounts is not idempotent.
Added a separate linux mount provider.
Define 'mounted?' method and used 'findmnt -rn' command to list mount points along with device.
Added linux_mount recipe to kitchen-tests.
Signed-off-by: antima-gupta <agupta@msystechnologies.com>
-rw-r--r-- | kitchen-tests/cookbooks/end_to_end/recipes/_linux_mount.rb | 20 | ||||
-rw-r--r-- | kitchen-tests/cookbooks/end_to_end/recipes/linux.rb | 1 | ||||
-rw-r--r-- | lib/chef/provider/mount/linux.rb | 58 | ||||
-rw-r--r-- | lib/chef/providers.rb | 1 | ||||
-rw-r--r-- | spec/unit/provider/mount/linux_spec.rb | 87 |
5 files changed, 167 insertions, 0 deletions
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_linux_mount.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_linux_mount.rb new file mode 100644 index 0000000000..207ef9d1fb --- /dev/null +++ b/kitchen-tests/cookbooks/end_to_end/recipes/_linux_mount.rb @@ -0,0 +1,20 @@ +mount "/proc" do + device "proc" + fstype "proc" + options %w{bind rw} + action %i{ mount enable } +end + +mount "/mnt" do + device "/tmp" + fstype "ext4" + options %w{bind rw} + action %i{ mount enable } +end + +mount "/mnt" do + device "/etc" + fstype "ext4" + options %w{bind rw} + action %i{ mount enable } +end
\ No newline at end of file diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb index 8fae36662c..bb94fb88d1 100644 --- a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb +++ b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb @@ -122,6 +122,7 @@ include_recipe "::_cron" include_recipe "::_ohai_hint" include_recipe "::_openssl" include_recipe "::_tests" +include_recipe "::_linux_mount" # at the moment these do not run properly in docker # we need to investigate if this is a snap on docker issue or a chef issue diff --git a/lib/chef/provider/mount/linux.rb b/lib/chef/provider/mount/linux.rb new file mode 100644 index 0000000000..25a03c08be --- /dev/null +++ b/lib/chef/provider/mount/linux.rb @@ -0,0 +1,58 @@ +# +# Author:: Joshua Timberman (<joshua@chef.io>) +# Copyright:: Copyright (c) Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require_relative "../mount" + +class Chef + class Provider + class Mount + class Linux < Chef::Provider::Mount::Mount + + provides :mount, os: "linux" + + def mounted? + mounted = false + + # "mount" outputs the mount points as real paths. Convert + # the mount_point of the resource to a real path in case it + # contains symlinks in its parents dirs. + real_mount_point = if ::File.exists? @new_resource.mount_point + ::File.realpath(@new_resource.mount_point) + else + @new_resource.mount_point + end + + shell_out!("findmnt -rn").stdout.each_line do |line| + case line + when /\A#{Regexp.escape(real_mount_point)}\s+#{device_mount_regex}\s/ + mounted = true + logger.trace("Special device #{device_logstring} mounted as #{real_mount_point}") + when %r{\A#{Regexp.escape(real_mount_point)}\s+([/\w])+\s} + mounted = false + logger.trace("Special device #{$~[1]} mounted as #{real_mount_point}") + when %r{\A#{Regexp.escape(real_mount_point)}\s+([/\w])+\[#{device_mount_regex}\]\s} + mounted = true + logger.trace("Bind device #{device_logstring} mounted as #{real_mount_point}") + end + end + @current_resource.mounted(mounted) + end + end + end + end +end diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb index d2e9f1991b..6b099fc2b7 100644 --- a/lib/chef/providers.rb +++ b/lib/chef/providers.rb @@ -119,6 +119,7 @@ require_relative "provider/mount/mount" require_relative "provider/mount/aix" require_relative "provider/mount/solaris" require_relative "provider/mount/windows" +require_relative "provider/mount/linux" require_relative "provider/remote_file/ftp" require_relative "provider/remote_file/sftp" diff --git a/spec/unit/provider/mount/linux_spec.rb b/spec/unit/provider/mount/linux_spec.rb new file mode 100644 index 0000000000..feb8d97167 --- /dev/null +++ b/spec/unit/provider/mount/linux_spec.rb @@ -0,0 +1,87 @@ +require "spec_helper" +require "ostruct" + +describe Chef::Provider::Mount::Linux do + before(:each) do + @node = Chef::Node.new + @events = Chef::EventDispatch::Dispatcher.new + @run_context = Chef::RunContext.new(@node, {}, @events) + + @new_resource = Chef::Resource::Mount.new("/tmp/foo") + @new_resource.device "/dev/sdz1" + @new_resource.device_type :device + @new_resource.fstype "ext3" + + @new_resource.supports remount: false + + @provider = Chef::Provider::Mount::Linux.new(@new_resource, @run_context) + + allow(::File).to receive(:exists?).with("/dev/sdz1").and_return true + allow(::File).to receive(:exists?).with("/tmp/foo").and_return true + allow(::File).to receive(:realpath).with("/dev/sdz1").and_return "/dev/sdz1" + allow(::File).to receive(:realpath).with("/tmp/foo").and_return "/tmp/foo" + end + + it "should set mounted true if the mount point is found in the mounts list" do + allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "/tmp/foo /dev/sdz1 type ext3 (rw)\n")) + @provider.load_current_resource + expect(@provider.current_resource.mounted).to be_truthy + end + + it "should set mounted false if another mount point beginning with the same path is found in the mounts list" do + allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "/tmp/foobar /dev/sdz1 type ext3 (rw)\n")) + @provider.load_current_resource + expect(@provider.current_resource.mounted).to be_falsey + end + + it "should set mounted true if the symlink target of the device is found in the mounts list" do + # expand the target path to correct specs on Windows + target = ::File.expand_path("/dev/mapper/target") + + allow(::File).to receive(:symlink?).with((@new_resource.device).to_s).and_return(true) + allow(::File).to receive(:readlink).with((@new_resource.device).to_s).and_return(target) + + allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "/tmp/foo #{target} type ext3 (rw)\n")) + @provider.load_current_resource + expect(@provider.current_resource.mounted).to be_truthy + end + + it "should set mounted true if the symlink target of the device is relative and is found in the mounts list - CHEF-4957" do + target = "xsdz1" + + # expand the target path to correct specs on Windows + absolute_target = ::File.expand_path("/dev/xsdz1") + + allow(::File).to receive(:symlink?).with((@new_resource.device).to_s).and_return(true) + allow(::File).to receive(:readlink).with((@new_resource.device).to_s).and_return(target) + + allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "/tmp/foo #{absolute_target} type ext3 (rw)\n")) + @provider.load_current_resource + expect(@provider.current_resource.mounted).to be_truthy + end + + it "should set mounted true if the mount point is found last in the mounts list" do + mount = "#{@new_resource.mount_point} /dev/sdy1 type ext3 (rw)\n" + mount << "#{@new_resource.mount_point} #{@new_resource.device} type ext3 (rw)\n" + + allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: mount)) + @provider.load_current_resource + expect(@provider.current_resource.mounted).to be_truthy + end + + it "should set mounted false if the mount point is not last in the mounts list" do + mount = "#{@new_resource.device} on #{@new_resource.mount_point} type ext3 (rw)\n" + mount << "/dev/sdy1 on #{@new_resource.mount_point} type ext3 (rw)\n" + + allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: mount)) + @provider.load_current_resource + expect(@provider.current_resource.mounted).to be_falsey + end + + it "mounted should be false if the mount point is not found in the mounts list" do + allow(@provider).to receive(:shell_out!).and_return(OpenStruct.new(stdout: "/dev/sdy1 on /tmp/foo type ext3 (rw)\n")) + @provider.load_current_resource + expect(@provider.current_resource.mounted).to be_falsey + end + +end
\ No newline at end of file |