summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlamont-granquist <lamont@scriptkiddie.org>2014-06-04 14:26:02 -0700
committerlamont-granquist <lamont@scriptkiddie.org>2014-06-04 14:26:02 -0700
commitf367d9a35edc6ec5f0ab2955bba364f0bb2f7fdf (patch)
treeb736a6a8ac6f5815cf35c7e52654ce5fe2c2cf8c
parent69e554b96d4ef919a87dce892e26331d73cc3054 (diff)
parentafda7ab3cdc5e88a044140dcba31fa8738142754 (diff)
downloadchef-f367d9a35edc6ec5f0ab2955bba364f0bb2f7fdf.tar.gz
Merge pull request #1451 from opscode/lcg/CHEF-2824
Add mount provider for Solaris OS and derivates
-rw-r--r--lib/chef/platform/provider_mapping.rb7
-rw-r--r--lib/chef/provider/mount/solaris.rb233
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource/mount.rb7
-rw-r--r--spec/functional/resource/mount_spec.rb25
-rw-r--r--spec/unit/provider/mount/solaris_spec.rb646
6 files changed, 907 insertions, 12 deletions
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index 4ebbde3486..a4b19192ff 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -276,6 +276,7 @@ class Chef
:solaris => {},
:openindiana => {
:default => {
+ :mount => Chef::Provider::Mount::Solaris,
:service => Chef::Provider::Service::Solaris,
:package => Chef::Provider::Package::Ips,
:cron => Chef::Provider::Cron::Solaris,
@@ -284,6 +285,7 @@ class Chef
},
:opensolaris => {
:default => {
+ :mount => Chef::Provider::Mount::Solaris,
:service => Chef::Provider::Service::Solaris,
:package => Chef::Provider::Package::Ips,
:cron => Chef::Provider::Cron::Solaris,
@@ -292,6 +294,7 @@ class Chef
},
:nexentacore => {
:default => {
+ :mount => Chef::Provider::Mount::Solaris,
:service => Chef::Provider::Service::Solaris,
:package => Chef::Provider::Package::Solaris,
:cron => Chef::Provider::Cron::Solaris,
@@ -300,6 +303,7 @@ class Chef
},
:omnios => {
:default => {
+ :mount => Chef::Provider::Mount::Solaris,
:service => Chef::Provider::Service::Solaris,
:package => Chef::Provider::Package::Ips,
:cron => Chef::Provider::Cron::Solaris,
@@ -309,6 +313,7 @@ class Chef
},
:solaris2 => {
:default => {
+ :mount => Chef::Provider::Mount::Solaris,
:service => Chef::Provider::Service::Solaris,
:package => Chef::Provider::Package::Ips,
:cron => Chef::Provider::Cron::Solaris,
@@ -316,6 +321,7 @@ class Chef
:user => Chef::Provider::User::Solaris,
},
"< 5.11" => {
+ :mount => Chef::Provider::Mount::Solaris,
:service => Chef::Provider::Service::Solaris,
:package => Chef::Provider::Package::Solaris,
:cron => Chef::Provider::Cron::Solaris,
@@ -325,6 +331,7 @@ class Chef
},
:smartos => {
:default => {
+ :mount => Chef::Provider::Mount::Solaris,
:service => Chef::Provider::Service::Solaris,
:package => Chef::Provider::Package::SmartOS,
:cron => Chef::Provider::Cron::Solaris,
diff --git a/lib/chef/provider/mount/solaris.rb b/lib/chef/provider/mount/solaris.rb
new file mode 100644
index 0000000000..85158eb564
--- /dev/null
+++ b/lib/chef/provider/mount/solaris.rb
@@ -0,0 +1,233 @@
+#
+# Author:: Hugo Fichter
+# Author:: Lamont Granquist (<lamont@getchef.com>)
+# Author:: Joshua Timberman (<joshua@getchef.com>)
+# Copyright:: Copyright (c) 2009-2014 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 'chef/provider/mount'
+require 'chef/log'
+require 'chef/mixin/shell_out'
+require 'forwardable'
+
+class Chef
+ class Provider
+ class Mount
+ class Solaris < Chef::Provider::Mount
+ include Chef::Mixin::ShellOut
+ extend Forwardable
+
+ VFSTAB = "/etc/vfstab".freeze
+
+ def_delegator :@new_resource, :device, :device
+ def_delegator :@new_resource, :device_type, :device_type
+ def_delegator :@new_resource, :dump, :dump
+ def_delegator :@new_resource, :fstype, :fstype
+ def_delegator :@new_resource, :mount_point, :mount_point
+ def_delegator :@new_resource, :options, :options
+ def_delegator :@new_resource, :pass, :pass
+
+ def load_current_resource
+ @current_resource = Chef::Resource::Mount.new(new_resource.name)
+ current_resource.mount_point(mount_point)
+ current_resource.device(device)
+ current_resource.device_type(device_type)
+ update_current_resource_state
+ end
+
+ def define_resource_requirements
+ requirements.assert(:mount, :remount) do |a|
+ a.assertion { !device_should_exist? || ::File.exist?(device) }
+ a.failure_message(Chef::Exceptions::Mount, "Device #{device} does not exist")
+ a.whyrun("Assuming device #{device} would have been created")
+ end
+
+ requirements.assert(:mount, :remount) do |a|
+ a.assertion { ::File.exist?(mount_point) }
+ a.failure_message(Chef::Exceptions::Mount, "Mount point #{mount_point} does not exist")
+ a.whyrun("Assuming mount point #{mount_point} would have been created")
+ end
+ end
+
+ def mount_fs
+ actual_options = options || []
+ actual_options.delete("noauto")
+ command = "mount -F #{fstype}"
+ command << " -o #{actual_options.join(',')}" unless actual_options.empty?
+ command << " #{device} #{mount_point}"
+ shell_out!(command)
+ end
+
+ def umount_fs
+ shell_out!("umount #{mount_point}")
+ end
+
+ def remount_fs
+ # FIXME: what about options like "-o remount,logging" to enable logging on a UFS device?
+ shell_out!("mount -o remount #{mount_point}")
+ end
+
+ def enable_fs
+ if !mount_options_unchanged?
+ # we are enabling because our options have changed, so disable first then re-enable.
+ # XXX: this should be refactored to be the responsibility of the caller
+ disable_fs if current_resource.enabled
+ end
+
+ auto = options.nil? || ! options.include?("noauto")
+ actual_options = unless options.nil?
+ options.delete("noauto")
+ options
+ end
+
+ autostr = auto ? 'yes' : 'no'
+ passstr = pass == 0 ? "-" : pass
+ optstr = (actual_options.nil? || actual_options.empty?) ? "-" : actual_options.join(',')
+
+ etc_tempfile do |f|
+ f.write(IO.read(VFSTAB).chomp)
+ f.puts("\n#{device}\t-\t#{mount_point}\t#{fstype}\t#{passstr}\t#{autostr}\t#{optstr}")
+ f.close
+ # move, preserving modes of destination file
+ mover = Chef::FileContentManagement::Deploy.strategy(true)
+ mover.deploy(f.path, VFSTAB)
+ end
+
+ end
+
+ def disable_fs
+ contents = []
+
+ found = false
+ ::File.readlines(VFSTAB).reverse_each do |line|
+ if !found && line =~ /^#{device_regex}\s+\S+\s+#{Regexp.escape(mount_point)}/
+ found = true
+ Chef::Log.debug("#{new_resource} is removed from vfstab")
+ next
+ end
+ contents << line
+ end
+
+ if found
+ etc_tempfile do |f|
+ f.write(contents.reverse.join(''))
+ f.close
+ # move, preserving modes of destination file
+ mover = Chef::FileContentManagement::Deploy.strategy(true)
+ mover.deploy(f.path, VFSTAB)
+ end
+ else
+ # this is likely some kind of internal error, since we should only call disable_fs when there
+ # the filesystem we want to disable is enabled.
+ Chef::Log.warn("#{new_resource} did not find the mountpoint to disable in the vfstab")
+ end
+ end
+
+ def etc_tempfile
+ yield Tempfile.open("vfstab", "/etc")
+ end
+
+ def mount_options_unchanged?
+ current_resource.fstype == fstype and
+ current_resource.options == options and
+ current_resource.dump == dump and
+ current_resource.pass == pass
+ end
+
+ def update_current_resource_state
+ current_resource.mounted(mounted?)
+ ( enabled, fstype, options, pass ) = read_vfstab_status
+ current_resource.enabled(enabled)
+ current_resource.fstype(fstype)
+ current_resource.options(options)
+ current_resource.pass(pass)
+ end
+
+ def enabled?
+ read_vfstab_status[0]
+ end
+
+ def mounted?
+ mounted = false
+ shell_out!("mount -v").stdout.each_line do |line|
+ # <device> on <mountpoint> type <fstype> <options> on <date>
+ # /dev/dsk/c1t0d0s0 on / type ufs read/write/setuid/devices/intr/largefiles/logging/xattr/onerror=panic/dev=700040 on Tue May 1 11:33:55 2012
+ case line
+ when /^#{device_regex}\s+on\s+#{Regexp.escape(mount_point)}\s+/
+ Chef::Log.debug("Special device #{device} is mounted as #{mount_point}")
+ mounted = true
+ when /^([\/\w]+)\son\s#{Regexp.escape(mount_point)}\s+/
+ Chef::Log.debug("Special device #{$1} is mounted as #{mount_point}")
+ mounted = false
+ end
+ end
+ mounted
+ end
+
+ private
+
+ def read_vfstab_status
+ # Check to see if there is a entry in /etc/vfstab. Last entry for a volume wins.
+ enabled = false
+ fstype = options = pass = nil
+ ::File.foreach(VFSTAB) do |line|
+ case line
+ when /^[#\s]/
+ next
+ # solaris /etc/vfstab format:
+ # device device mount FS fsck mount mount
+ # to mount to fsck point type pass at boot options
+ when /^#{device_regex}\s+[-\/\w]+\s+#{Regexp.escape(mount_point)}\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/
+ enabled = true
+ fstype = $1
+ options = $4
+ # Store the 'mount at boot' column from vfstab as the 'noauto' option
+ # in current_resource.options (linux style)
+ if $3 == "yes"
+ if options.nil? || options.empty?
+ options = "noauto"
+ else
+ options += ",noauto"
+ end
+ end
+ pass = ( $2 == "-" ) ? 0 : $2.to_i
+ Chef::Log.debug("Found mount #{device} to #{mount_point} in #{VFSTAB}")
+ next
+ when /^[-\/\w]+\s+[-\/\w]+\s+#{Regexp.escape(mount_point)}\s+/
+ # if we find a mountpoint on top of our mountpoint, then we are not enabled
+ enabled = false
+ Chef::Log.debug("Found conflicting mount point #{mount_point} in #{VFSTAB}")
+ end
+ end
+ [ enabled, fstype, options, pass ]
+ end
+
+ def device_should_exist?
+ !%w{tmpfs nfs ctfs proc mntfs objfs sharefs fd smbfs}.include?(fstype)
+ end
+
+ def device_regex
+ if ::File.symlink?(device)
+ "(?:#{Regexp.escape(device)}|#{Regexp.escape(::File.expand_path(::File.readlink(device),::File.dirname(device)))})"
+ else
+ Regexp.escape(device)
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index d6e70c1d4e..8989aa3beb 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -103,6 +103,7 @@ require 'chef/provider/group/windows'
require 'chef/provider/mount/mount'
require 'chef/provider/mount/aix'
+require 'chef/provider/mount/solaris'
require 'chef/provider/mount/windows'
require 'chef/provider/deploy/revision'
diff --git a/lib/chef/resource/mount.rb b/lib/chef/resource/mount.rb
index 507c5329e3..9eafe07253 100644
--- a/lib/chef/resource/mount.rb
+++ b/lib/chef/resource/mount.rb
@@ -65,10 +65,15 @@ class Chef
def device_type(arg=nil)
real_arg = arg.kind_of?(String) ? arg.to_sym : arg
+ valid_devices = if RUBY_PLATFORM =~ /solaris/i
+ [ :device ]
+ else
+ [ :device, :label, :uuid ]
+ end
set_or_return(
:device_type,
real_arg,
- :equal_to => [ :device, :label, :uuid ]
+ :equal_to => valid_devices
)
end
diff --git a/spec/functional/resource/mount_spec.rb b/spec/functional/resource/mount_spec.rb
index 199ccbd37e..caa139d029 100644
--- a/spec/functional/resource/mount_spec.rb
+++ b/spec/functional/resource/mount_spec.rb
@@ -21,7 +21,7 @@ require 'chef/mixin/shell_out'
require 'tmpdir'
# run this test only for following platforms.
-include_flag = !(['ubuntu', 'centos', 'aix'].include?(ohai[:platform]))
+include_flag = !(['ubuntu', 'centos', 'aix', 'solaris2'].include?(ohai[:platform]))
describe Chef::Resource::Mount, :requires_root, :external => include_flag do
@@ -52,6 +52,9 @@ describe Chef::Resource::Mount, :requires_root, :external => include_flag do
end
fstype = "tmpfs"
shell_out!("mkfs -q #{device} 512")
+ when "solaris2"
+ device = "swap"
+ fstype = "tmpfs"
else
end
[device, fstype]
@@ -71,11 +74,10 @@ describe Chef::Resource::Mount, :requires_root, :external => include_flag do
end
# platform specific validations.
- def mount_should_exists(mount_point, device, fstype = nil, options = nil)
+ def mount_should_exist(mount_point, device, fstype = nil, options = nil)
validation_cmd = "mount | grep #{mount_point} | grep #{device} "
validation_cmd << " | grep #{fstype} " unless fstype.nil?
validation_cmd << " | grep #{options.join(',')} " unless options.nil? || options.empty?
- puts "validation_cmd = #{validation_cmd}"
expect(shell_out(validation_cmd).exitstatus).to eq(0)
end
@@ -87,6 +89,8 @@ describe Chef::Resource::Mount, :requires_root, :external => include_flag do
case ohai[:platform]
when 'aix'
mount_config = "/etc/filesystems"
+ when 'solaris2'
+ mount_config = "/etc/vfstab"
else
mount_config = "/etc/fstab"
end
@@ -119,7 +123,7 @@ describe Chef::Resource::Mount, :requires_root, :external => include_flag do
provider
end
- def current_resource
+ let(:current_resource) do
provider.load_current_resource
provider.current_resource
end
@@ -138,7 +142,6 @@ describe Chef::Resource::Mount, :requires_root, :external => include_flag do
end
end
end
-
end
after(:all) do
@@ -156,28 +159,28 @@ describe Chef::Resource::Mount, :requires_root, :external => include_flag do
current_resource.mounted.should be_false
new_resource.run_action(:mount)
new_resource.should be_updated
- mount_should_exists(new_resource.mount_point, new_resource.device)
+ mount_should_exist(new_resource.mount_point, new_resource.device)
end
-
end
- describe "when the filesystem should be remounted and the resource supports remounting" do
+ # don't run the remount tests on solaris2 (tmpfs does not support remount)
+ describe "when the filesystem should be remounted and the resource supports remounting", :external => ohai[:platform] == "solaris2" do
it "should remount the filesystem if it is mounted" do
new_resource.run_action(:mount)
- mount_should_exists(new_resource.mount_point, new_resource.device)
+ mount_should_exist(new_resource.mount_point, new_resource.device)
new_resource.supports[:remount] = true
new_resource.options "rw,log=NULL" if ohai[:platform] == 'aix'
new_resource.run_action(:remount)
- mount_should_exists(new_resource.mount_point, new_resource.device, nil, (ohai[:platform] == 'aix') ? new_resource.options : nil)
+ mount_should_exist(new_resource.mount_point, new_resource.device, nil, (ohai[:platform] == 'aix') ? new_resource.options : nil)
end
end
describe "when the target state is a unmounted filesystem" do
it "should umount the filesystem if it is mounted" do
new_resource.run_action(:mount)
- mount_should_exists(new_resource.mount_point, new_resource.device)
+ mount_should_exist(new_resource.mount_point, new_resource.device)
new_resource.run_action(:umount)
mount_should_not_exists(new_resource.mount_point)
diff --git a/spec/unit/provider/mount/solaris_spec.rb b/spec/unit/provider/mount/solaris_spec.rb
new file mode 100644
index 0000000000..e7dd17c746
--- /dev/null
+++ b/spec/unit/provider/mount/solaris_spec.rb
@@ -0,0 +1,646 @@
+#
+# Author:: Lamont Granquist (<lamont@getchef.com>)
+# Copyright:: Copyright (c) 2008-2014 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 'spec_helper'
+require 'ostruct'
+
+describe Chef::Provider::Mount::Solaris do
+ let(:node) { Chef::Node.new }
+
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+
+ let(:device_type) { :device }
+
+ let(:fstype) { "ufs" }
+
+ let(:device) { "/dev/dsk/c0t2d0s7" }
+
+ let(:mountpoint) { "/mnt/foo" }
+
+ let(:options) { nil }
+
+ let(:new_resource) {
+ new_resource = Chef::Resource::Mount.new(mountpoint)
+ new_resource.device device
+ new_resource.device_type device_type
+ new_resource.fstype fstype
+ new_resource.options options
+
+ new_resource.supports :remount => false
+ new_resource
+ }
+
+ let(:provider) {
+ Chef::Provider::Mount::Solaris.new(new_resource, run_context)
+ }
+
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ #device device mount FS fsck mount mount
+ #to mount to fsck point type pass at boot options
+ #
+ fd - /dev/fd fd - no -
+ /proc - /proc proc - no -
+ # swap
+ /dev/dsk/c0t0d0s1 - - swap - no -
+ # root
+ /dev/dsk/c0t0d0s0 /dev/rdsk/c0t0d0s0 / ufs 1 no -
+ # tmpfs
+ swap - /tmp tmpfs - yes -
+ # nfs
+ cartman:/share2 - /cartman nfs - yes rw,soft
+ # ufs
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ EOF
+ }
+
+ let(:vfstab_file) {
+ t = Tempfile.new("rspec-vfstab")
+ t.write(vfstab_file_contents)
+ t.close
+ t
+ }
+
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t0d0s0 on / type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
+ /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
+ EOF
+ }
+
+ before do
+ stub_const("Chef::Provider::Mount::Solaris::VFSTAB", vfstab_file.path )
+ provider.stub(:shell_out!).with("mount -v").and_return(OpenStruct.new(:stdout => mount_output))
+ File.stub(:symlink?).with(device).and_return(false)
+ File.stub(:exist?).and_call_original # Tempfile.open on ruby 1.8.7 calls File.exist?
+ File.stub(:exist?).with(device).and_return(true)
+ File.stub(:exist?).with(mountpoint).and_return(true)
+ expect(File).to_not receive(:exists?)
+ end
+
+ describe "#define_resource_requirements" do
+ before do
+ # we're not testing the actual actions so stub them all out
+ [:mount_fs, :umount_fs, :remount_fs, :enable_fs, :disable_fs].each {|m| provider.stub(m) }
+ end
+
+ it "run_action(:mount) should raise an error if the device does not exist" do
+ File.stub(:exist?).with(device).and_return(false)
+ expect { provider.run_action(:mount) }.to raise_error(Chef::Exceptions::Mount)
+ end
+
+ it "run_action(:remount) should raise an error if the device does not exist" do
+ File.stub(:exist?).with(device).and_return(false)
+ expect { provider.run_action(:remount) }.to raise_error(Chef::Exceptions::Mount)
+ end
+
+ it "run_action(:mount) should raise an error if the mountpoint does not exist" do
+ File.stub(:exist?).with(mountpoint).and_return false
+ expect { provider.run_action(:mount) }.to raise_error(Chef::Exceptions::Mount)
+ end
+
+ it "run_action(:remount) should raise an error if the mountpoint does not exist" do
+ File.stub(:exist?).with(mountpoint).and_return false
+ expect { provider.run_action(:remount) }.to raise_error(Chef::Exceptions::Mount)
+ end
+
+ %w{tmpfs nfs ctfs proc mntfs objfs sharefs fd smbfs}.each do |ft|
+ context "when the device has a fstype of #{ft}" do
+ let(:fstype) { ft }
+ let(:device) { "something_that_is_not_a_file" }
+
+ before do
+ expect(File).to_not receive(:exist?).with(device)
+ end
+
+ it "run_action(:mount) should not raise an error" do
+ expect { provider.run_action(:mount) }.to_not raise_error
+ end
+
+ it "run_action(:remount) should not raise an error" do
+ expect { provider.run_action(:remount) }.to_not raise_error
+ end
+ end
+ end
+
+ end
+
+ describe "#load_current_resource" do
+ context "when loading a normal UFS filesystem" do
+
+ before do
+ provider.load_current_resource
+ end
+
+ it "should create a current_resource of type Chef::Resource::Mount" do
+ expect(provider.current_resource).to be_a(Chef::Resource::Mount)
+ end
+
+ it "should set the name on the current_resource" do
+ provider.current_resource.name.should == mountpoint
+ end
+
+ it "should set the mount_point on the current_resource" do
+ provider.current_resource.mount_point.should == mountpoint
+ end
+
+ it "should set the device on the current_resource" do
+ provider.current_resource.device.should == device
+ end
+
+ it "should set the device_type on the current_resource" do
+ provider.current_resource.device_type.should == device_type
+ end
+
+ it "should set the mounted status on the current_resource" do
+ expect(provider.current_resource.mounted).to be_true
+ end
+
+ it "should set the enabled status on the current_resource" do
+ expect(provider.current_resource.enabled).to be_true
+ end
+
+ it "should set the fstype field on the current_resource" do
+ expect(provider.current_resource.fstype).to eql("ufs")
+ end
+
+ it "should set the options field on the current_resource" do
+ expect(provider.current_resource.options).to eql(["-", "noauto"])
+ end
+
+ it "should set the pass field on the current_resource" do
+ expect(provider.current_resource.pass).to eql(2)
+ end
+
+ it "should not throw an exception when the device does not exist - CHEF-1565" do
+ File.stub(:exist?).with(device).and_return(false)
+ expect { provider.load_current_resource }.to_not raise_error
+ end
+
+ it "should not throw an exception when the mount point does not exist" do
+ File.stub(:exist?).with(mountpoint).and_return false
+ expect { provider.load_current_resource }.to_not raise_error
+ end
+ end
+
+ context "when the device is an smbfs mount" do
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ //solarsystem/tmp on /mnt type smbfs read/write/setuid/devices/dev=5080000 on Tue Mar 29 11:40:18 2011
+ EOF
+ }
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ //WORKGROUP;username:password@host/share - /mountpoint smbfs - no fileperms=0777,dirperms=0777
+ EOF
+ }
+
+ it "should work at some point in the future" do
+ pending "SMBFS mounts on solaris look like they will need some future code work and more investigation"
+ end
+ end
+
+ context "when the device is an NFS mount" do
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ cartman:/share2 on /cartman type nfs rsize=32768,wsize=32768,NFSv4,dev=4000004 on Tue Mar 29 11:40:18 2011
+ EOF
+ }
+
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ cartman:/share2 - /cartman nfs - yes rw,soft
+ EOF
+ }
+
+ let(:fstype) { "nfs" }
+
+ let(:device) { "cartman:/share2" }
+
+ let(:mountpoint) { "/cartman" }
+
+ before do
+ provider.load_current_resource
+ end
+
+ it "should set the name on the current_resource" do
+ provider.current_resource.name.should == mountpoint
+ end
+
+ it "should set the mount_point on the current_resource" do
+ provider.current_resource.mount_point.should == mountpoint
+ end
+
+ it "should set the device on the current_resource" do
+ provider.current_resource.device.should == device
+ end
+
+ it "should set the device_type on the current_resource" do
+ provider.current_resource.device_type.should == device_type
+ end
+
+ it "should set the mounted status on the current_resource" do
+ expect(provider.current_resource.mounted).to be_true
+ end
+
+ it "should set the enabled status on the current_resource" do
+ expect(provider.current_resource.enabled).to be_true
+ end
+
+ it "should set the fstype field on the current_resource" do
+ expect(provider.current_resource.fstype).to eql("nfs")
+ end
+
+ it "should set the options field on the current_resource" do
+ expect(provider.current_resource.options).to eql(["rw", "soft", "noauto"])
+ end
+
+ it "should set the pass field on the current_resource" do
+ # is this correct or should it be nil?
+ expect(provider.current_resource.pass).to eql(0)
+ end
+
+ end
+
+ context "when the device is symlink" do
+
+ let(:target) { "/dev/mapper/target" }
+
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ #{target} on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
+ EOF
+ }
+
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ #{target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ EOF
+ }
+
+ before do
+ File.should_receive(:symlink?).with(device).at_least(:once).and_return(true)
+ File.should_receive(:readlink).with(device).at_least(:once).and_return(target)
+
+ provider.load_current_resource()
+ end
+
+ it "should set mounted true if the symlink target of the device is found in the mounts list" do
+ expect(provider.current_resource.mounted).to be_true
+ end
+
+ it "should set enabled true if the symlink target of the device is found in the vfstab" do
+ expect(provider.current_resource.enabled).to be_true
+ end
+
+ it "should have the correct mount options" do
+ expect(provider.current_resource.options).to eql(["-", "noauto"])
+ end
+ end
+
+ context "when the device is a relative symlink" do
+ let(:target) { "foo" }
+
+ let(:absolute_target) { File.expand_path(target, File.dirname(device)) }
+
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ #{absolute_target} on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
+ EOF
+ }
+
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ #{absolute_target} /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ EOF
+ }
+
+ before do
+ File.should_receive(:symlink?).with(device).at_least(:once).and_return(true)
+ File.should_receive(:readlink).with(device).at_least(:once).and_return(target)
+
+ provider.load_current_resource()
+ end
+
+ it "should set mounted true if the symlink target of the device is found in the mounts list" do
+ expect(provider.current_resource.mounted).to be_true
+ end
+
+ it "should set enabled true if the symlink target of the device is found in the vfstab" do
+ expect(provider.current_resource.enabled).to be_true
+ end
+
+ it "should have the correct mount options" do
+ expect(provider.current_resource.options).to eql(["-", "noauto"])
+ end
+ end
+
+ context "when the matching mount point is last in the mounts list" do
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t0d0s0 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
+ /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
+ EOF
+ }
+ it "should set mounted true" do
+ provider.load_current_resource()
+ provider.current_resource.mounted.should be_true
+ end
+ end
+
+ context "when the matching mount point is not last in the mounts list" do
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t2d0s7 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
+ /dev/dsk/c0t0d0s0 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Tue Jul 31 22:34:46 2012
+ EOF
+ }
+ it "should set mounted false" do
+ provider.load_current_resource()
+ provider.current_resource.mounted.should be_false
+ end
+ end
+
+ context "when the matching mount point is not in the mounts list (mountpoint wrong)" do
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t2d0s7 on /mnt/foob type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
+ EOF
+ }
+ it "should set mounted false" do
+ provider.load_current_resource()
+ provider.current_resource.mounted.should be_false
+ end
+ end
+
+ context "when the matching mount point is not in the mounts list (raw device wrong)" do
+ let(:mount_output) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t2d0s72 on /mnt/foo type ufs read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Tue Jul 31 22:34:46 2012
+ EOF
+ }
+ it "should set mounted false" do
+ provider.load_current_resource()
+ provider.current_resource.mounted.should be_false
+ end
+ end
+
+ context "when the mount point is last in fstab" do
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ EOF
+ }
+
+ it "should set enabled to true" do
+ provider.load_current_resource
+ provider.current_resource.enabled.should be_true
+ end
+ end
+
+ context "when the mount point is not last in fstab and is a substring of another mount" do
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo/bar ufs 2 yes -
+ EOF
+ }
+
+ it "should set enabled to true" do
+ provider.load_current_resource
+ provider.current_resource.enabled.should be_true
+ end
+ end
+
+ context "when the mount point is not last in fstab" do
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s72 /mnt/foo ufs 2 yes -
+ EOF
+ }
+
+ it "should set enabled to false" do
+ provider.load_current_resource
+ provider.current_resource.enabled.should be_false
+ end
+ end
+
+ context "when the mount point is not in fstab, but the mountpoint is a substring of one that is" do
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foob ufs 2 yes -
+ EOF
+ }
+
+ it "should set enabled to false" do
+ provider.load_current_resource
+ provider.current_resource.enabled.should be_false
+ end
+ end
+
+ context "when the mount point is not in fstab, but the device is a substring of one that is" do
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ /dev/dsk/c0t2d0s72 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ EOF
+ }
+
+ it "should set enabled to false" do
+ provider.load_current_resource
+ provider.current_resource.enabled.should be_false
+ end
+ end
+
+ context "when the mountpoint line is commented out" do
+ let(:vfstab_file_contents) {
+ <<-EOF.gsub /^\s*/, ''
+ #/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -
+ EOF
+ }
+
+ it "should set enabled to false" do
+ provider.load_current_resource
+ provider.current_resource.enabled.should be_false
+ end
+ end
+ end
+
+ context "after the mount's state has been discovered" do
+ describe "mount_fs" do
+ it "should mount the filesystem" do
+ provider.should_receive(:shell_out!).with("mount -F #{fstype} -o defaults #{device} #{mountpoint}")
+ provider.mount_fs()
+ end
+
+ it "should mount the filesystem with options if options were passed" do
+ options = "logging,noatime,largefiles,nosuid,rw,quota"
+ new_resource.options(options.split(/,/))
+ provider.should_receive(:shell_out!).with("mount -F #{fstype} -o #{options} #{device} #{mountpoint}")
+ provider.mount_fs()
+ end
+
+ it "should delete the 'noauto' magic option" do
+ options = "rw,noauto"
+ new_resource.options(%w{rw noauto})
+ provider.should_receive(:shell_out!).with("mount -F #{fstype} -o rw #{device} #{mountpoint}")
+ provider.mount_fs()
+ end
+ end
+
+ describe "umount_fs" do
+ it "should umount the filesystem if it is mounted" do
+ provider.should_receive(:shell_out!).with("umount #{mountpoint}")
+ provider.umount_fs()
+ end
+ end
+
+ describe "remount_fs" do
+ it "should use mount -o remount" do
+ provider.should_receive(:shell_out!).with("mount -o remount #{new_resource.mount_point}")
+ provider.remount_fs
+ end
+ end
+
+ describe "when enabling the fs" do
+ context "in the typical case" do
+ let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" }
+
+ let(:this_mount) { "/dev/dsk/c0t2d0s7\t-\t/mnt/foo\tufs\t2\tyes\tdefaults\n" }
+
+ let(:vfstab_file_contents) { [other_mount].join("\n") }
+
+ before do
+ provider.stub(:etc_tempfile).and_yield(Tempfile.open("vfstab"))
+ provider.load_current_resource
+ provider.enable_fs
+ end
+
+ it "should leave the other mountpoint alone" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(other_mount)}/)
+ end
+
+ it "should enable the mountpoint we care about" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(this_mount)}/)
+ end
+ end
+
+ context "when the mount has options=noauto" do
+ let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" }
+
+ let(:this_mount) { "/dev/dsk/c0t2d0s7\t-\t/mnt/foo\tufs\t2\tno\t-\n" }
+
+ let(:options) { [ "noauto" ] }
+
+ let(:vfstab_file_contents) { [other_mount].join("\n") }
+
+ before do
+ provider.stub(:etc_tempfile).and_yield(Tempfile.open("vfstab"))
+ provider.load_current_resource
+ provider.enable_fs
+ end
+
+ it "should leave the other mountpoint alone" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(other_mount)}/)
+ end
+
+ it "should enable the mountpoint we care about" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(this_mount)}/)
+ end
+ end
+ end
+
+ describe "when disabling the fs" do
+ context "in the typical case" do
+ let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" }
+
+ let(:this_mount) { "/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -" }
+
+ let(:vfstab_file_contents) { [other_mount, this_mount].join("\n") }
+
+ before do
+ provider.stub(:etc_tempfile).and_yield(Tempfile.open("vfstab"))
+ provider.disable_fs
+ end
+
+ it "should leave the other mountpoint alone" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(other_mount)}/)
+ end
+
+ it "should disable the mountpoint we care about" do
+ IO.read(vfstab_file.path).should_not match(/^#{Regexp.escape(this_mount)}/)
+ end
+ end
+
+ context "when there is a commented out line" do
+ let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" }
+
+ let(:this_mount) { "/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -" }
+
+ let(:comment) { "#/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -" }
+
+ let(:vfstab_file_contents) { [other_mount, this_mount, comment].join("\n") }
+
+ before do
+ provider.stub(:etc_tempfile).and_yield(Tempfile.open("vfstab"))
+ provider.disable_fs
+ end
+
+ it "should leave the other mountpoint alone" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(other_mount)}/)
+ end
+
+ it "should disable the mountpoint we care about" do
+ IO.read(vfstab_file.path).should_not match(/^#{Regexp.escape(this_mount)}/)
+ end
+
+ it "should keep the comment" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(comment)}/)
+ end
+ end
+
+ context "when there is a duplicated line" do
+ let(:other_mount) { "/dev/dsk/c0t2d0s0 /dev/rdsk/c0t2d0s0 / ufs 2 yes -" }
+
+ let(:this_mount) { "/dev/dsk/c0t2d0s7 /dev/rdsk/c0t2d0s7 /mnt/foo ufs 2 yes -" }
+
+ let(:vfstab_file_contents) { [this_mount, other_mount, this_mount].join("\n") }
+
+ before do
+ provider.stub(:etc_tempfile).and_yield(Tempfile.open("vfstab"))
+ provider.disable_fs
+ end
+
+ it "should leave the other mountpoint alone" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(other_mount)}/)
+ end
+
+ it "should still match the duplicated mountpoint" do
+ IO.read(vfstab_file.path).should match(/^#{Regexp.escape(this_mount)}/)
+ end
+
+ it "should have removed the last line" do
+ IO.read(vfstab_file.path).should eql( "#{this_mount}\n#{other_mount}\n" )
+ end
+ end
+ end
+ end
+end