diff options
author | Tim Smith <tsmith@chef.io> | 2020-08-28 08:40:16 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-28 08:40:16 -0700 |
commit | 19066bd9430baea8d63e8981515d78d7c5ca2997 (patch) | |
tree | b0d8325a0377f767dbd201c9a5abc63e0544a420 | |
parent | a92e03c00eb7633e529732a12cbc6a56646e9187 (diff) | |
parent | 4e6234d500475ec772ae2f7d81bfb91067b4a12c (diff) | |
download | chef-19066bd9430baea8d63e8981515d78d7c5ca2997.tar.gz |
Merge pull request #10171 from MsysTechnologiesllc/antima/bind_mounts_not_idempotent_fixes
Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r-- | kitchen-tests/cookbooks/end_to_end/recipes/_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 | 63 | ||||
-rw-r--r-- | lib/chef/providers.rb | 1 | ||||
-rw-r--r-- | spec/unit/provider/mount/linux_spec.rb | 97 |
5 files changed, 182 insertions, 0 deletions
diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/_mount.rb b/kitchen-tests/cookbooks/end_to_end/recipes/_mount.rb new file mode 100644 index 0000000000..207ef9d1fb --- /dev/null +++ b/kitchen-tests/cookbooks/end_to_end/recipes/_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..ba1f5e84f7 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 "::_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..3199024f1b --- /dev/null +++ b/lib/chef/provider/mount/linux.rb @@ -0,0 +1,63 @@ +# +# Author:: Antima Gupta (<agupta@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" + + # Check to see if the volume is mounted. + # "findmnt" outputs the mount points with volume. + # Convert the mount_point of the resource to a real path in case it + # contains symlinks in its parents dirs. + + def mounted? + mounted = false + + 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 + # Permalink for device already mounted to mount point for : https://rubular.com/r/L0RNnD4gf2DJGl + 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}") + # Permalink for multiple devices mounted to the same mount point(i.e. '/proc') https://rubular.com/r/a356yzspU7N9TY + when %r{\A#{Regexp.escape(real_mount_point)}\s+([/\w])+\s} + mounted = false + logger.trace("Special device #{$~[1]} mounted as #{real_mount_point}") + # Permalink for bind device mounted to an existing mount point: https://rubular.com/r/QAE0ilL3sm3Ldz + 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..1141175780 --- /dev/null +++ b/spec/unit/provider/mount/linux_spec.rb @@ -0,0 +1,97 @@ +require "spec_helper" + +describe Chef::Provider::Mount::Linux do + + let(:run_context) do + node = Chef::Node.new + events = Chef::EventDispatch::Dispatcher.new + run_context = Chef::RunContext.new(node, {}, events) + end + + let(:new_resource) do + 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 + new_resource + end + + let(:provider) do + Chef::Provider::Mount::Linux.new(new_resource, run_context) + end + + before(:each) do + 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 + + context "to see if the volume is mounted" do + + it "should set mounted true if the mount point is found in the mounts list" do + allow(provider).to receive(:shell_out!).and_return(double(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(double(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(double(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(double(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(double(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(double(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(double(stdout: "/dev/sdy1 on /tmp/foo type ext3 (rw)\n")) + provider.load_current_resource + expect(provider.current_resource.mounted).to be_falsey + end + + end + +end |