diff --git a/lib/puppet/provider/mount.rb b/lib/puppet/provider/mount.rb index 81d93b5c1..65296eed2 100644 --- a/lib/puppet/provider/mount.rb +++ b/lib/puppet/provider/mount.rb @@ -1,95 +1,49 @@ # Created by Luke Kanies on 2006-11-12. # Copyright (c) 2006. All rights reserved. require 'puppet' # A module just to store the mount/unmount methods. Individual providers # still need to add the mount commands manually. module Puppet::Provider::Mount - def mount - # Make sure the fstab file & entry exists - create - - if correctly_mounted? - # Nothing to do! - else - if anything_mounted? - unmount - - # We attempt to create the mount point here, because unmounting - # certain file systems/devices can cause the mount point to be - # deleted - ::FileUtils.mkdir_p(resource[:name]) - end - - mount! - end - end - # This only works when the mount point is synced to the fstab. - def mount! + def mount # Manually pass the mount options in, since some OSes *cough*OS X*cough* don't # read from /etc/fstab but still want to use this type. args = [] args << "-o" << self.options if self.options and self.options != :absent args << resource[:name] - flush if respond_to?(:flush) mountcmd(*args) + case get(:ensure) + when :absent; set(:ensure => :ghost) + when :unmounted; set(:ensure => :mounted) + end end def remount info "Remounting" if resource[:remounts] == :true mountcmd "-o", "remount", resource[:name] else unmount mount end end # This only works when the mount point is synced to the fstab. def unmount - umount resource[:name] - end + umount(resource[:name]) - # Is anything currently mounted at this point? - def anything_mounted? - platform = Facter.value("operatingsystem") - name = resource[:name] - mounts = mountcmd.split("\n").find do |line| - case platform - when "Darwin" - line =~ / on #{name} / or line =~ %r{ on /private/var/automount#{name}} - when "Solaris", "HP-UX" - # Yes, Solaris does list mounts as "mount_point on device" - line =~ /^#{name} on / - when "AIX" - line.split(/\s+/)[2] == name - else - line =~ / on #{name} / - end + # Update property hash for future queries (e.g. refresh is called) + case get(:ensure) + when :mounted; set(:ensure => :unmounted) + when :ghost; set(:ensure => :absent) end end - # Is the desired thing mounted at this point? - def correctly_mounted? - platform = Facter.value("operatingsystem") - name = resource[:name] - device = resource[:device] - mounts = mountcmd.split("\n").find do |line| - case platform - when "Darwin" - line =~ /^#{device} on #{name} / or line =~ %r{^#{device} on /private/var/automount#{name}} - when "Solaris", "HP-UX" - # Yes, Solaris does list mounts as "mount_point on device" - line =~ /^#{name} on #{device}/ - when "AIX" - line.split(/\s+/)[2] == name && - line.split(/\s+/)[1] == device - else - line =~ /^#{device} on #{name} / - end - end + # Is the mount currently mounted? + def mounted? + [:mounted, :ghost].include?(get(:ensure)) end end diff --git a/lib/puppet/provider/mount/parsed.rb b/lib/puppet/provider/mount/parsed.rb index 69a6fc06b..42e543c15 100755 --- a/lib/puppet/provider/mount/parsed.rb +++ b/lib/puppet/provider/mount/parsed.rb @@ -1,42 +1,100 @@ require 'puppet/provider/parsedfile' require 'puppet/provider/mount' fstab = nil case Facter.value(:operatingsystem) when "Solaris"; fstab = "/etc/vfstab" else fstab = "/etc/fstab" end Puppet::Type.type(:mount).provide( :parsed, :parent => Puppet::Provider::ParsedFile, :default_target => fstab, :filetype => :flat ) do include Puppet::Provider::Mount commands :mountcmd => "mount", :umount => "umount" - @platform = Facter["operatingsystem"].value - case @platform + case Facter["operatingsystem"] when "Solaris" @fields = [:device, :blockdevice, :name, :fstype, :pass, :atboot, :options] else @fields = [:device, :name, :fstype, :options, :dump, :pass] @fielddefaults = [ nil ] * 4 + [ "0", "2" ] end text_line :comment, :match => /^\s*#/ text_line :blank, :match => /^\s*$/ optional_fields = @fields - [:device, :name, :blockdevice] mandatory_fields = @fields - optional_fields # fstab will ignore lines that have fewer than the mandatory number of columns, # so we should, too. field_pattern = '(\s*(?>\S+))' text_line :incomplete, :match => /^(?!#{field_pattern}{#{mandatory_fields.length}})/ record_line self.name, :fields => @fields, :separator => /\s+/, :joiner => "\t", :optional => optional_fields + + # Every entry in fstab is :unmounted until we can prove different + def self.prefetch_hook(target_records) + target_records.collect do |record| + record[:ensure] = :unmounted if record[:record_type] == :parsed + record + end + end + + def self.prefetch(resources = nil) + # Get providers for all resources the user defined and that match + # a record in /etc/fstab. + super + # We need to do two things now: + # - Update ensure from :unmounted to :mounted if the resource is mounted + # - Check for mounted devices that are not in fstab and + # set ensure to :ghost (if the user wants to add an entry + # to fstab we need to know if the device was mounted before) + mountinstances.each do |hash| + if mount = resources[hash[:name]] + case mount.provider.get(:ensure) + when :absent # Mount not in fstab + mount.provider.set(:ensure => :ghost) + when :unmounted # Mount in fstab + mount.provider.set(:ensure => :mounted) + end + end + end + end + + def self.mountinstances + # XXX: Will not work for mount points that have spaces in path (does fstab support this anyways?) + regex = case Facter.value(:operatingsystem) + when "Darwin" + / on (?:\/private\/var\/automount)?(\S*)/ + when "Solaris", "HP-UX" + /^(\S*) on / + when "AIX" + /^(?:\S*\s+\S+\s+)(\S+)/ + else + / on (\S*)/ + end + instances = [] + mount_output = mountcmd.split("\n") + if mount_output.length >= 2 and mount_output[1] =~ /^[- \t]*$/ + # On some OSes (e.g. AIX) mount output begins with a header line + # followed by a line consisting of dashes and whitespace. + # Discard these two lines. + mount_output[0..1] = [] + end + mount_output.each do |line| + if match = regex.match(line) and name = match.captures.first + instances << {:name => name, :mounted => :yes} # Only :name is important here + else + raise Puppet::Error, "Could not understand line #{line} from mount output" + end + end + instances + end end diff --git a/lib/puppet/type/mount.rb b/lib/puppet/type/mount.rb index 10eed5373..98a1f2509 100755 --- a/lib/puppet/type/mount.rb +++ b/lib/puppet/type/mount.rb @@ -1,219 +1,239 @@ module Puppet # We want the mount to refresh when it changes. newtype(:mount, :self_refresh => true) do @doc = "Manages mounted filesystems, including putting mount information into the mount table. The actual behavior depends on the value of the 'ensure' parameter. Note that if a `mount` receives an event from another resource, it will try to remount the filesystems if `ensure` is set to `mounted`." feature :refreshable, "The provider can remount the filesystem.", :methods => [:remount] # Use the normal parent class, because we actually want to # call code when sync is called. newproperty(:ensure) do desc "Control what to do with this mount. Set this attribute to `umounted` to make sure the filesystem is in the filesystem table but not mounted (if the filesystem is currently mounted, it will be unmounted). Set it to `absent` to unmount (if necessary) and remove the filesystem from the fstab. Set to `mounted` to add it to the fstab and mount it. Set to `present` to add to fstab but not change mount/unmount status" + # IS -> SHOULD In Sync Action + # ghost -> present NO create + # absent -> present NO create + # (mounted -> present YES) + # (unmounted -> present YES) newvalue(:defined) do provider.create return :mount_created end aliasvalue :present, :defined + # IS -> SHOULD In Sync Action + # ghost -> unmounted NO create, unmount + # absent -> unmounted NO create + # mounted -> unmounted NO unmount newvalue(:unmounted) do - if provider.anything_mounted? - syncothers + case self.retrieve + when :ghost # (not in fstab but mounted) + provider.create + @resource.flush provider.unmount return :mount_unmounted - else + when nil, :absent # (not in fstab and not mounted) provider.create return :mount_created + when :mounted # (in fstab and mounted) + provider.unmount + syncothers # I guess it's more likely that the mount was originally mounted with + # the wrong attributes so I sync AFTER the umount + return :mount_unmounted + else + raise Puppet::Error, "Unexpected change from #{current_value} to unmounted}" end end + # IS -> SHOULD In Sync Action + # ghost -> absent NO unmount + # mounted -> absent NO provider.destroy AND unmount + # unmounted -> absent NO provider.destroy newvalue(:absent, :event => :mount_deleted) do - provider.unmount if provider.anything_mounted? - - provider.destroy + current_value = self.retrieve + provider.unmount if provider.mounted? + provider.destroy unless current_value == :ghost end + # IS -> SHOULD In Sync Action + # ghost -> mounted NO provider.create + # absent -> mounted NO provider.create AND mount + # unmounted -> mounted NO mount newvalue(:mounted, :event => :mount_mounted) do + # Create the mount point if it does not already exist. + current_value = self.retrieve + provider.create if [nil, :absent, :ghost].include?(current_value) + syncothers - provider.mount + # The fs can be already mounted if it was absent but mounted + provider.mount unless provider.mounted? end + # insync: mounted -> present + # unmounted -> present def insync?(is) - if should == :defined and is != :absent + if should == :defined and [:mounted,:unmounted].include?(is) true else super end end - def retrieve - # We need to special case :mounted; if we're absent, we still - # want - curval = super() - if curval == :absent - return :absent - elsif provider.correctly_mounted? - return :mounted - else - return :unmounted - end - end - def syncothers # We have to flush any changes to disk. currentvalues = @resource.retrieve_resource # Determine if there are any out-of-sync properties. oos = @resource.send(:properties).find_all do |prop| unless currentvalues.include?(prop) raise Puppet::DevError, "Parent has property %s but it doesn't appear in the current values", [prop.name] end if prop.name == :ensure false else ! prop.safe_insync?(currentvalues[prop]) end end.each { |prop| prop.sync }.length @resource.flush if oos > 0 end end newproperty(:device) do desc "The device providing the mount. This can be whatever device is supporting by the mount, including network devices or devices specified by UUID rather than device path, depending on the operating system." end # Solaris specifies two devices, not just one. newproperty(:blockdevice) do desc "The device to fsck. This is property is only valid on Solaris, and in most cases will default to the correct value." # Default to the device but with "dsk" replaced with "rdsk". defaultto do if Facter["operatingsystem"].value == "Solaris" device = @resource.value(:device) if device =~ %r{/dsk/} device.sub(%r{/dsk/}, "/rdsk/") else nil end else nil end end end newproperty(:fstype) do desc "The mount type. Valid values depend on the operating system. This is a required option." end newproperty(:options) do desc "Mount options for the mounts, as they would appear in the fstab." end newproperty(:pass) do desc "The pass in which the mount is checked." defaultto { 0 if @resource.managed? } end newproperty(:atboot) do desc "Whether to mount the mount at boot. Not all platforms support this." end newproperty(:dump) do desc "Whether to dump the mount. Not all platform support this. Valid values are `1` or `0`. or `2` on FreeBSD, Default is `0`." if Facter["operatingsystem"].value == "FreeBSD" newvalue(%r{(0|1|2)}) else newvalue(%r{(0|1)}) end newvalue(%r{(0|1)}) defaultto { 0 if @resource.managed? } end newproperty(:target) do desc "The file in which to store the mount table. Only used by those providers that write to disk." defaultto { if @resource.class.defaultprovider.ancestors.include?(Puppet::Provider::ParsedFile) @resource.class.defaultprovider.default_target else nil end } end newparam(:name) do desc "The mount path for the mount." isnamevar end newparam(:path) do desc "The deprecated name for the mount point. Please use `name` now." def value=(value) warning "'path' is deprecated for mounts. Please use 'name'." @resource[:name] = value super end end newparam(:remounts) do desc "Whether the mount can be remounted `mount -o remount`. If this is false, then the filesystem will be unmounted and remounted manually, which is prone to failure." newvalues(:true, :false) defaultto do case Facter.value(:operatingsystem) when "FreeBSD", "Darwin", "AIX" false else true end end end def refresh # Only remount if we're supposed to be mounted. - provider.remount if self.should(:fstype) != "swap" and provider.anything_mounted? + provider.remount if self.should(:fstype) != "swap" and provider.mounted? end def value(name) name = symbolize(name) ret = nil if property = @parameters[name] return property.value end end end end diff --git a/spec/fixtures/unit/provider/mount/mount-output.other.txt b/spec/fixtures/unit/provider/mount/mount-output.other.txt deleted file mode 100644 index 0e4dff0c5..000000000 --- a/spec/fixtures/unit/provider/mount/mount-output.other.txt +++ /dev/null @@ -1,14 +0,0 @@ -/dev/sda1 on / type ext4 (rw,errors=remount-ro,commit=0) -proc on /proc type proc (rw,noexec,nosuid,nodev) -none on /sys type sysfs (rw,noexec,nosuid,nodev) -fusectl on /sys/fs/fuse/connections type fusectl (rw) -none on /sys/kernel/debug type debugfs (rw) -none on /sys/kernel/security type securityfs (rw) -none on /dev type devtmpfs (rw,mode=0755) -none on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=0620) -none on /dev/shm type tmpfs (rw,nosuid,nodev) -none on /var/run type tmpfs (rw,nosuid,mode=0755) -none on /var/lock type tmpfs (rw,noexec,nosuid,nodev) -none on /proc/fs/vmblock/mountPoint type vmblock (rw) -binfmt_misc on /proc/sys/fs/binfmt_misc type binfmt_misc (rw,noexec,nosuid,nodev) -/dev/sda2 on /usr type ext4 (rw,errors=remount-ro,commit=0) diff --git a/spec/fixtures/unit/provider/mount/mount-output.solaris.txt b/spec/fixtures/unit/provider/mount/mount-output.solaris.txt deleted file mode 100644 index 477926138..000000000 --- a/spec/fixtures/unit/provider/mount/mount-output.solaris.txt +++ /dev/null @@ -1,16 +0,0 @@ -/ on rpool/ROOT/opensolaris read/write/setuid/devices/dev=2d90002 on Wed Dec 31 16:00:00 1969 -/devices on /devices read/write/setuid/devices/dev=4a00000 on Thu Feb 17 14:34:02 2011 -/dev on /dev read/write/setuid/devices/dev=4a40000 on Thu Feb 17 14:34:02 2011 -/system/contract on ctfs read/write/setuid/devices/dev=4ac0001 on Thu Feb 17 14:34:02 2011 -/proc on proc read/write/setuid/devices/dev=4b00000 on Thu Feb 17 14:34:02 2011 -/etc/mnttab on mnttab read/write/setuid/devices/dev=4b40001 on Thu Feb 17 14:34:02 2011 -/etc/svc/volatile on swap read/write/setuid/devices/xattr/dev=4b80001 on Thu Feb 17 14:34:02 2011 -/system/object on objfs read/write/setuid/devices/dev=4bc0001 on Thu Feb 17 14:34:02 2011 -/etc/dfs/sharetab on sharefs read/write/setuid/devices/dev=4c00001 on Thu Feb 17 14:34:02 2011 -/lib/libc.so.1 on /usr/lib/libc/libc_hwcap1.so.1 read/write/setuid/devices/dev=2d90002 on Thu Feb 17 14:34:14 2011 -/dev/fd on fd read/write/setuid/devices/dev=4d00001 on Thu Feb 17 14:34:18 2011 -/tmp on swap read/write/setuid/devices/xattr/dev=4b80002 on Thu Feb 17 14:34:19 2011 -/var/run on swap read/write/setuid/devices/xattr/dev=4b80003 on Thu Feb 17 14:34:19 2011 -/export on rpool/export read/write/setuid/devices/nonbmand/exec/xattr/atime/dev=2d90006 on Thu Feb 17 14:37:48 2011 -/export/home on rpool/export/home read/write/setuid/devices/nonbmand/exec/xattr/atime/dev=2d90007 on Thu Feb 17 14:37:48 2011 -/rpool on rpool read/write/setuid/devices/nonbmand/exec/xattr/atime/dev=2d90009 on Thu Feb 17 14:37:48 2011 diff --git a/spec/unit/provider/mount/parsed_spec.rb b/spec/unit/provider/mount/parsed_spec.rb index 069d9495a..cf29bd358 100755 --- a/spec/unit/provider/mount/parsed_spec.rb +++ b/spec/unit/provider/mount/parsed_spec.rb @@ -1,182 +1,277 @@ #!/usr/bin/env ruby # # Created by Luke Kanies on 2007-9-12. # Copyright (c) 2006. All rights reserved. require File.dirname(__FILE__) + '/../../../spec_helper' +require 'puppet_spec/files' require 'puppettest/support/utils' require 'puppettest/fileparsing' module ParsedMountTesting include PuppetTest::Support::Utils include PuppetTest::FileParsing + include PuppetSpec::Files def fake_fstab - os = Facter['operatingsystem'] + os = Facter.value(:operatingsystem) if os == "Solaris" name = "solaris.fstab" elsif os == "FreeBSD" name = "freebsd.fstab" else # Catchall for other fstabs name = "linux.fstab" end - oldpath = @provider_class.default_target fakefile(File::join("data/types/mount", name)) end - def mkmountargs - mount = nil - - if defined?(@pcount) - @pcount += 1 + def fake_mountoutput + os = Facter.value(:operatingsystem) + if os == "Darwin" + name = "darwin.mount" + elsif os == "HP-UX" + name = "hpux.mount" + elsif os == "Solaris" + name = "solaris.mount" + elsif os == "AIX" + name = "aix.mount" else - @pcount = 1 - end - args = { - :name => "/fspuppet#{@pcount}", - :device => "/dev/dsk#{@pcount}", - } - - @provider_class.fields(:parsed).each do |field| - args[field] = "fake#{field}#{@pcount}" unless args.include? field + # Catchall for other fstabs + name = "linux.mount" end - - args + fakefile(File::join("data/providers/mount/parsed", name)) end - def mkmount - hash = mkmountargs - #hash[:provider] = @provider_class.name - - fakeresource = stub :type => :mount, :name => hash[:name] - fakeresource.stubs(:[]).with(:name).returns(hash[:name]) - fakeresource.stubs(:should).with(:target).returns(nil) - - mount = @provider_class.new(fakeresource) - hash[:record_type] = :parsed - hash[:ensure] = :present - mount.property_hash = hash - - mount - end - - # Here we just create a fake host type that answers to all of the methods - # but does not modify our actual system. - def mkfaketype - @provider.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - end end provider_class = Puppet::Type.type(:mount).provider(:parsed) describe provider_class do + before :each do @mount_class = Puppet::Type.type(:mount) - @provider_class = @mount_class.provider(:parsed) + @provider = @mount_class.provider(:parsed) end + # LAK:FIXME I can't mock Facter because this test happens at parse-time. + it "should default to /etc/vfstab on Solaris" do + pending "This test only works on Solaris" unless Facter.value(:operatingsystem) == 'Solaris' + Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/vfstab' + end - describe provider_class do - include ParsedMountTesting + it "should default to /etc/fstab on anything else" do + pending "This test does not work on Solaris" if Facter.value(:operatingsystem) == 'Solaris' + Puppet::Type.type(:mount).provider(:parsed).default_target.should == '/etc/fstab' + end - it "should be able to parse all of the example mount tabs" do - tab = fake_fstab - @provider = @provider_class + describe "when parsing a line" do - # LAK:FIXME Again, a relatively bad test, but I don't know how to rspec-ify this. - # I suppose this is more of an integration test? I dunno. - fakedataparse(tab) do - # Now just make we've got some mounts we know will be there - hashes = @provider_class.target_records(tab).find_all { |i| i.is_a? Hash } - (hashes.length > 0).should be_true - root = hashes.find { |i| i[:name] == "/" } + it "should not crash on incomplete lines in fstab" do + parse = @provider.parse <<-FSTAB +/dev/incomplete +/dev/device name +FSTAB + lambda{ @provider.to_line(parse[0]) }.should_not raise_error + end + + + describe "on Solaris", :if => Facter.value(:operatingsystem) == 'Solaris' do - proc { @provider_class.to_file(hashes) }.should_not raise_error + before :each do + @example_line = "/dev/dsk/c0d0s0 /dev/rdsk/c0d0s0 \t\t / \t ufs 1 no\t-" end - end - # LAK:FIXME I can't mock Facter because this test happens at parse-time. - it "should default to /etc/vfstab on Solaris and /etc/fstab everywhere else" do - should = case Facter.value(:operatingsystem) - when "Solaris"; "/etc/vfstab" - else - "/etc/fstab" - end - Puppet::Type.type(:mount).provider(:parsed).default_target.should == should - end + it "should extract device from the first field" do + @provider.parse_line(@example_line)[:device].should == '/dev/dsk/c0d0s0' + end - it "should not crash on incomplete lines in fstab" do - parse = @provider_class.parse <<-FSTAB -/dev/incomplete -/dev/device name - FSTAB + it "should extract blockdevice from second field" do + @provider.parse_line(@example_line)[:blockdevice].should == "/dev/rdsk/c0d0s0" + end + + it "should extract name from third field" do + @provider.parse_line(@example_line)[:name].should == "/" + end + + it "should extract fstype from fourth field" do + @provider.parse_line(@example_line)[:fstype].should == "ufs" + end + + it "should extract pass from fifth field" do + @provider.parse_line(@example_line)[:pass].should == "1" + end + + it "should extract atboot from sixth field" do + @provider.parse_line(@example_line)[:atboot].should == "no" + end + + it "should extract options from seventh field" do + @provider.parse_line(@example_line)[:options].should == "-" + end - lambda{ @provider_class.to_line(parse[0]) }.should_not raise_error end - end - describe provider_class, " when mounting an absent filesystem" do - include ParsedMountTesting + describe "on other platforms than Solaris", :if => Facter.value(:operatingsystem) != 'Solaris' do + + before :each do + @example_line = "/dev/vg00/lv01\t/spare \t \t ext3 defaults\t1 2" + end + + it "should extract device from the first field" do + @provider.parse_line(@example_line)[:device].should == '/dev/vg00/lv01' + end + + it "should extract name from second field" do + @provider.parse_line(@example_line)[:name].should == "/spare" + end + + it "should extract fstype from third field" do + @provider.parse_line(@example_line)[:fstype].should == "ext3" + end - # #730 - Make sure 'flush' is called when a mount is moving from absent to mounted - it "should flush the fstab to disk" do - mount = mkmount + it "should extract options from fourth field" do + @provider.parse_line(@example_line)[:options].should == "defaults" + end - # Mark the mount as absent - mount.property_hash[:ensure] = :absent + it "should extract dump from fifth field" do + @provider.parse_line(@example_line)[:dump].should == "1" + end - mount.stubs(:mountcmd) # just so we don't actually try to mount anything + it "should extract options from sixth field" do + @provider.parse_line(@example_line)[:pass].should == "2" + end - mount.expects(:flush) - mount.mount! end + end - describe provider_class, " when modifying the filesystem tab" do + describe "mountinstances" do include ParsedMountTesting - before do - Puppet.settings.stubs(:use) - # Never write to disk, only to RAM. - #@provider_class.stubs(:filetype).returns(Puppet::Util::FileType.filetype(:ram)) - @provider_class.stubs(:target_object).returns(Puppet::Util::FileType.filetype(:ram).new("eh")) - @provider_class.clear - @mount = mkmount - @target = @provider_class.default_target + it "should get name from mountoutput found on Solaris" do + Facter.stubs(:value).with(:operatingsystem).returns 'Solaris' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts.size.should == 6 + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/proc', :mounted => :yes } + mounts[2].should == { :name => '/etc/mnttab', :mounted => :yes } + mounts[3].should == { :name => '/tmp', :mounted => :yes } + mounts[4].should == { :name => '/export/home', :mounted => :yes } + mounts[5].should == { :name => '/ghost', :mounted => :yes } + end + + it "should get name from mountoutput found on HP-UX" do + Facter.stubs(:value).with(:operatingsystem).returns 'HP-UX' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts.size.should == 17 + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/devices', :mounted => :yes } + mounts[2].should == { :name => '/dev', :mounted => :yes } + mounts[3].should == { :name => '/system/contract', :mounted => :yes } + mounts[4].should == { :name => '/proc', :mounted => :yes } + mounts[5].should == { :name => '/etc/mnttab', :mounted => :yes } + mounts[6].should == { :name => '/etc/svc/volatile', :mounted => :yes } + mounts[7].should == { :name => '/system/object', :mounted => :yes } + mounts[8].should == { :name => '/etc/dfs/sharetab', :mounted => :yes } + mounts[9].should == { :name => '/lib/libc.so.1', :mounted => :yes } + mounts[10].should == { :name => '/dev/fd', :mounted => :yes } + mounts[11].should == { :name => '/tmp', :mounted => :yes } + mounts[12].should == { :name => '/var/run', :mounted => :yes } + mounts[13].should == { :name => '/export', :mounted => :yes } + mounts[14].should == { :name => '/export/home', :mounted => :yes } + mounts[15].should == { :name => '/rpool', :mounted => :yes } + mounts[16].should == { :name => '/ghost', :mounted => :yes } + end + + it "should get name from mountoutput found on Darwin" do + Facter.stubs(:value).with(:operatingsystem).returns 'Darwin' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts.size.should == 6 + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/dev', :mounted => :yes } + mounts[2].should == { :name => '/net', :mounted => :yes } + mounts[3].should == { :name => '/home', :mounted => :yes } + mounts[4].should == { :name => '/usr', :mounted => :yes } + mounts[5].should == { :name => '/ghost', :mounted => :yes } end - it "should write the mount to disk when :flush is called" do - old_text = @provider_class.target_object(@provider_class.default_target).read + it "should get name from mountoutput found on Linux" do + Facter.stubs(:value).with(:operatingsystem).returns 'Gentoo' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/lib64/rc/init.d', :mounted => :yes } + mounts[2].should == { :name => '/sys', :mounted => :yes } + mounts[3].should == { :name => '/usr/portage', :mounted => :yes } + mounts[4].should == { :name => '/ghost', :mounted => :yes } + end - @mount.flush + it "should get name from mountoutput found on AIX" do + Facter.stubs(:value).with(:operatingsystem).returns 'AIX' + @provider.stubs(:mountcmd).returns(File.read(fake_mountoutput)) + mounts = @provider.mountinstances + mounts[0].should == { :name => '/', :mounted => :yes } + mounts[1].should == { :name => '/tmp', :mounted => :yes } + mounts[2].should == { :name => '/home', :mounted => :yes } + mounts[3].should == { :name => '/usr', :mounted => :yes } + mounts[4].should == { :name => '/usr/code', :mounted => :yes } + end - text = @provider_class.target_object(@provider_class.default_target).read - text.should == old_text + @mount.class.to_line(@mount.property_hash) + "\n" + it "should raise an error if a line is not understandable" do + @provider.stubs(:mountcmd).returns("bazinga!") + lambda { @provider.mountinstances }.should raise_error Puppet::Error end + end - describe provider_class, " when parsing information about the root filesystem", :if => Facter["operatingsystem"].value != "Darwin" do + describe "when prefetching" do include ParsedMountTesting - before do - @mount = @mount_class.new :name => "/" - @provider = @mount.provider + before :each do + # Note: we have to stub default_target before creating resources + # because it is used by Puppet::Type::Mount.new to populate the + # :target property. + @provider.stubs(:default_target).returns fake_fstab + + @res_ghost = Puppet::Type::Mount.new(:name => '/ghost') # in no fake fstab + @res_mounted = Puppet::Type::Mount.new(:name => '/') # in every fake fstab + @res_unmounted = Puppet::Type::Mount.new(:name => '/boot') # in every fake fstab + @res_absent = Puppet::Type::Mount.new(:name => '/absent') # in no fake fstab + + # Simulate transaction.rb:prefetch + @resource_hash = {} + [@res_ghost, @res_mounted, @res_unmounted, @res_absent].each do |resource| + @resource_hash[resource.name] = resource + end + + @provider.stubs(:mountcmd).returns File.read(fake_mountoutput) end - it "should have a filesystem tab" do - FileTest.should be_exist(@provider_class.default_target) + it "should set :ensure to :unmounted if found in fstab but not mounted" do + @provider.prefetch(@resource_hash) + @res_unmounted.provider.get(:ensure).should == :unmounted end - it "should find the root filesystem" do - @provider_class.prefetch("/" => @mount) - @mount.provider.property_hash[:ensure].should == :present + it "should set :ensure to :mounted if found in fstab and mounted" do + @provider.prefetch(@resource_hash) + @res_ghost.provider.get(:ensure).should == :ghost end - it "should determine that the root fs is mounted" do - @provider_class.prefetch("/" => @mount) - @mount.provider.should be_anything_mounted + it "should set :ensure to :ghost if not found in fstab but mounted" do + @provider.prefetch(@resource_hash) + @res_mounted.provider.get(:ensure).should == :mounted end + + it "should set :ensure to :absent if not found in fstab and not mounted" do + @provider.prefetch(@resource_hash) + @res_absent.provider.get(:ensure).should == :absent + end + end + end diff --git a/spec/unit/provider/mount_spec.rb b/spec/unit/provider/mount_spec.rb index 1f2501765..3fc8a8664 100755 --- a/spec/unit/provider/mount_spec.rb +++ b/spec/unit/provider/mount_spec.rb @@ -1,202 +1,146 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' -require 'puppet_spec/files' require 'puppet/provider/mount' describe Puppet::Provider::Mount do - include PuppetSpec::Files - before :each do + @mounter = Object.new + @mounter.extend(Puppet::Provider::Mount) + @name = "/" - @resource = Puppet::Type.type(:mount).new( - :name => '/', - :device => '/dev/sda1', - :target => tmpfile("mount_provider") - ) + @resource = stub 'resource' + @resource.stubs(:[]).with(:name).returns(@name) - @mounter = Puppet::Type.type(:mount).defaultprovider().new(@resource) + @mounter.stubs(:resource).returns(@resource) end - describe "when calling mount!" do - it "should use the 'mountcmd' method to mount" do - @mounter.stubs(:options).returns(nil) - @mounter.expects(:mountcmd) + describe Puppet::Provider::Mount, " when mounting" do - @mounter.mount! + before :each do + @mounter.stubs(:get).with(:ensure).returns(:mounted) end - it "should flush before mounting if a flush method exists" do - @mounter.meta_def(:flush) { } - @mounter.expects(:flush) - @mounter.stubs(:mountcmd) + it "should use the 'mountcmd' method to mount" do @mounter.stubs(:options).returns(nil) + @mounter.expects(:mountcmd) - @mounter.mount! + @mounter.mount end it "should add the options following '-o' if they exist and are not set to :absent" do @mounter.stubs(:options).returns("ro") @mounter.expects(:mountcmd).with { |*ary| ary[0] == "-o" and ary[1] == "ro" } - @mounter.mount! + @mounter.mount end it "should specify the filesystem name to the mount command" do @mounter.stubs(:options).returns(nil) @mounter.expects(:mountcmd).with { |*ary| ary[-1] == @name } - @mounter.mount! + @mounter.mount + end + + it "should update the :ensure state to :mounted if it was :unmounted before" do + @mounter.expects(:mountcmd) + @mounter.stubs(:options).returns(nil) + @mounter.expects(:get).with(:ensure).returns(:unmounted) + @mounter.expects(:set).with(:ensure => :mounted) + @mounter.mount + end + + it "should update the :ensure state to :ghost if it was :absent before" do + @mounter.expects(:mountcmd) + @mounter.stubs(:options).returns(nil) + @mounter.expects(:get).with(:ensure).returns(:absent) + @mounter.expects(:set).with(:ensure => :ghost) + @mounter.mount end + end - describe "when remounting" do + describe Puppet::Provider::Mount, " when remounting" do + it "should use '-o remount' if the resource specifies it supports remounting" do @mounter.stubs(:info) - @resource[:remounts] = true + @resource.stubs(:[]).with(:remounts).returns(:true) @mounter.expects(:mountcmd).with("-o", "remount", @name) @mounter.remount end it "should unmount and mount if the resource does not specify it supports remounting" do @mounter.stubs(:info) - @resource[:remounts] = false + @resource.stubs(:[]).with(:remounts).returns(false) @mounter.expects(:unmount) @mounter.expects(:mount) @mounter.remount end it "should log that it is remounting" do - @resource[:remounts] = true + @resource.stubs(:[]).with(:remounts).returns(:true) @mounter.stubs(:mountcmd) @mounter.expects(:info).with("Remounting") @mounter.remount end end - describe "when unmounting" do + describe Puppet::Provider::Mount, " when unmounting" do + + before :each do + @mounter.stubs(:get).with(:ensure).returns(:unmounted) + end + it "should call the :umount command with the resource name" do @mounter.expects(:umount).with(@name) @mounter.unmount end - end - %w{Darwin Solaris HP-UX AIX Other}.each do |platform| - describe "on #{platform}" do - before :each do - case platform - when 'Darwin' - mount_fixture = 'mount-output.darwin.txt' - @mount_device = '/dev/disk0s3' - @mount_point = '/usr' - when 'Solaris' - mount_fixture = 'mount-output.solaris.txt' - @mount_device = 'swap' - @mount_point = '/tmp' - when 'HP-UX' - mount_fixture = 'mount-output.hp-ux.txt' - @mount_device = 'swap' - @mount_point = '/tmp' - when 'AIX' - mount_fixture = 'mount-output.aix.txt' - @mount_device = '/dev/hd2' - @mount_point = '/usr' - when 'Other' - mount_fixture = 'mount-output.other.txt' - @mount_device = '/dev/sda2' - @mount_point = '/usr' - end - @mount_data = File.read(File.join(File.dirname(__FILE__), '..', '..', 'fixtures', 'unit', 'provider', 'mount', mount_fixture)) - Facter.stubs(:value).with("operatingsystem").returns(platform) - end - - describe "when the correct thing is mounted" do - before :each do - @mounter.expects(:mountcmd).returns(@mount_data) - @resource.stubs(:[]).with(:name).returns(@mount_point) - @resource.stubs(:[]).with(:device).returns(@mount_device) - end - - it "should say anything_mounted?" do - @mounter.should be_anything_mounted - end - - it "should say correctly_mounted?" do - @mounter.should be_correctly_mounted - end - end - - describe "when the wrong thing is mounted" do - before :each do - @mounter.expects(:mountcmd).returns(@mount_data) - @resource.stubs(:[]).with(:name).returns(@mount_point) - @resource.stubs(:[]).with(:device).returns('/dev/bogus/thing') - end - - it "should say anything_mounted?" do - @mounter.should be_anything_mounted - end - - it "should not say correctly_mounted?" do - @mounter.should_not be_correctly_mounted - end - end - - describe "when nothing is mounted" do - before :each do - @mounter.expects(:mountcmd).returns(@mount_data) - @resource.stubs(:[]).with(:name).returns('/bogus/location') - @resource.stubs(:[]).with(:device).returns(@mount_device) - end - - it "should not say anything_mounted?" do - @mounter.should_not be_anything_mounted - end - - it "should not say correctly_mounted?" do - @mounter.should_not be_correctly_mounted - end - end + it "should update the :ensure state to :absent if it was :ghost before" do + @mounter.expects(:umount).with(@name).returns true + @mounter.expects(:get).with(:ensure).returns(:ghost) + @mounter.expects(:set).with(:ensure => :absent) + @mounter.unmount end - end - - describe "when mounting a device" do - it "should not mount! or unmount anything when the correct device is mounted" do - @mounter.stubs(:correctly_mounted?).returns(true) - @mounter.expects(:anything_mounted?).never - @mounter.expects(:create).once - @mounter.expects(:mount!).never - @mounter.expects(:unmount).never - FileUtils.expects(:mkdir_p).never - - @mounter.mount + it "should update the :ensure state to :unmounted if it was :mounted before" do + @mounter.expects(:umount).with(@name).returns true + @mounter.expects(:get).with(:ensure).returns(:mounted) + @mounter.expects(:set).with(:ensure => :unmounted) + @mounter.unmount end - it "should mount the device when nothing is mounted at the desired point" do - @mounter.stubs(:correctly_mounted?).returns(false) - @mounter.stubs(:anything_mounted?).returns(false) + end - @mounter.expects(:create).once - @mounter.expects(:mount!).once - @mounter.expects(:unmount).never - FileUtils.expects(:mkdir_p).never + describe Puppet::Provider::Mount, " when determining if it is mounted" do - @mounter.mount + it "should query the property_hash" do + @mounter.expects(:get).with(:ensure).returns(:mounted) + @mounter.mounted? + end + + it "should return true if prefetched value is :mounted" do + @mounter.stubs(:get).with(:ensure).returns(:mounted) + @mounter.mounted? == true end - it "should unmount the incorrect device and mount the correct device" do - @mounter.stubs(:correctly_mounted?).returns(false) - @mounter.stubs(:anything_mounted?).returns(true) + it "should return true if prefetched value is :ghost" do + @mounter.stubs(:get).with(:ensure).returns(:ghost) + @mounter.mounted? == true + end - @mounter.expects(:create).once - @mounter.expects(:mount!).once - @mounter.expects(:unmount).once - FileUtils.expects(:mkdir_p).with(@name).returns(true) + it "should return false if prefetched value is :absent" do + @mounter.stubs(:get).with(:ensure).returns(:absent) + @mounter.mounted? == false + end - @mounter.mount + it "should return false if prefetched value is :unmounted" do + @mounter.stubs(:get).with(:ensure).returns(:unmounted) + @mounter.mounted? == false end + end + end diff --git a/spec/unit/type/mount_spec.rb b/spec/unit/type/mount_spec.rb index c6d2b5ba0..7f9a0eba6 100755 --- a/spec/unit/type/mount_spec.rb +++ b/spec/unit/type/mount_spec.rb @@ -1,239 +1,332 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Type.type(:mount) do it "should have a :refreshable feature that requires the :remount method" do Puppet::Type.type(:mount).provider_feature(:refreshable).methods.should == [:remount] end it "should have no default value for :ensure" do mount = Puppet::Type.type(:mount).new(:name => "yay") mount.should(:ensure).should be_nil end + + it "should have :name as the only keyattribut" do + Puppet::Type.type(:mount).key_attributes.should == [:name] + end end describe Puppet::Type.type(:mount), "when validating attributes" do - [:name, :remounts].each do |param| + [:name, :remounts, :provider].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:mount).attrtype(param).should == :param end end [:ensure, :device, :blockdevice, :fstype, :options, :pass, :dump, :atboot, :target].each do |param| it "should have a #{param} property" do Puppet::Type.type(:mount).attrtype(param).should == :property end end end describe Puppet::Type.type(:mount)::Ensure, "when validating values" do before do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil Puppet::Type.type(:mount).defaultprovider.expects(:new).returns(@provider) end it "should alias :present to :defined as a value to :ensure" do mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) mount.should(:ensure).should == :defined end + it "should support :present as a value to :ensure" do + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :present) + end + + it "should support :defined as a value to :ensure" do + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :defined) + end + it "should support :unmounted as a value to :ensure" do - mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) - mount.should(:ensure).should == :unmounted + Puppet::Type.type(:mount).new(:name => "yay", :ensure => :unmounted) end it "should support :absent as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :absent) end it "should support :mounted as a value to :ensure" do Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) end end describe Puppet::Type.type(:mount)::Ensure do before :each do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :check => :ensure) @ensure = @mount.property(:ensure) end def mount_stub(params) Puppet::Type.type(:mount).validproperties.each do |prop| unless params[prop] params[prop] = :absent @mount[prop] = :absent end end params.each do |param, value| @provider.stubs(param).returns(value) end end - describe "when retrieving its current state" do - it "should return the provider's value if it is :absent" do - @provider.expects(:ensure).returns(:absent) - @ensure.retrieve.should == :absent - end + describe Puppet::Type.type(:mount)::Ensure, "when changing the host" do - it "should return :mounted if the provider indicates it is mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:correctly_mounted?).returns(true) - @ensure.retrieve.should == :mounted - end + def test_ensure_change(options) + @provider.stubs(:get).with(:ensure).returns options[:from] + @provider.stubs(:ensure).returns options[:from] + @provider.stubs(:mounted?).returns([:mounted,:ghost].include? options[:from]) + @provider.expects(:create).times(options[:create] || 0) + @provider.expects(:destroy).times(options[:destroy] || 0) + @provider.expects(:mount).times(options[:mount] || 0) + @provider.expects(:unmount).times(options[:unmount] || 0) + @ensure.stubs(:syncothers) + @ensure.should = options[:to] + @ensure.sync + end + + it "should create itself when changing from :ghost to :present" do + test_ensure_change(:from => :ghost, :to => :present, :create => 1) + end + + it "should create itself when changing from :absent to :present" do + test_ensure_change(:from => :absent, :to => :present, :create => 1) + end - it "should return :unmounted if the provider indicates it is not mounted and the value is not :absent" do - @provider.expects(:ensure).returns(:present) - @provider.expects(:correctly_mounted?).returns(false) - @ensure.retrieve.should == :unmounted - end - end + it "should create itself and unmount when changing from :ghost to :unmounted" do + test_ensure_change(:from => :ghost, :to => :unmounted, :create => 1, :unmount => 1) + end - describe "when changing the host" do - it "should destroy itself if it should be absent" do - @provider.stubs(:anything_mounted?).returns(false) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end + it "should unmount resource when changing from :mounted to :unmounted" do + test_ensure_change(:from => :mounted, :to => :unmounted, :unmount => 1) + end + + it "should create itself when changing from :absent to :unmounted" do + test_ensure_change(:from => :absent, :to => :unmounted, :create => 1) + end + + it "should unmount resource when changing from :ghost to :absent" do + test_ensure_change(:from => :ghost, :to => :absent, :unmount => 1) + end + + it "should unmount and destroy itself when changing from :mounted to :absent" do + test_ensure_change(:from => :mounted, :to => :absent, :destroy => 1, :unmount => 1) + end + + it "should destroy itself when changing from :unmounted to :absent" do + test_ensure_change(:from => :unmounted, :to => :absent, :destroy => 1) + end - it "should unmount itself before destroying if it is mounted and should be absent" do - @provider.expects(:anything_mounted?).returns(true) - @provider.expects(:unmount) - @provider.expects(:destroy) - @ensure.should = :absent - @ensure.sync - end + it "should create itself when changing from :ghost to :mounted" do + test_ensure_change(:from => :ghost, :to => :mounted, :create => 1) + end - it "should create itself if it is absent and should be defined" do - @provider.stubs(:ensure).returns(:absent) - @provider.stubs(:anything_mounted?).returns(true) + it "should create itself and mount when changing from :absent to :mounted" do + test_ensure_change(:from => :absent, :to => :mounted, :create => 1, :mount => 1) + end - @provider.stubs(:anything_mounted?).returns(false) - @provider.expects(:create) - @ensure.should = :defined - @ensure.sync - end + it "should mount resource when changing from :unmounted to :mounted" do + test_ensure_change(:from => :unmounted, :to => :mounted, :mount => 1) + end - it "should not unmount itself if it is mounted and should be defined" do - @provider.stubs(:ensure).returns(:mounted) - @provider.stubs(:anything_mounted?).returns(true) - @provider.stubs(:create) - @provider.expects(:mount).never - @provider.expects(:unmount).never - @ensure.should = :defined - @ensure.sync - end + it "should be in sync if it is :absent and should be :absent" do + @ensure.should = :absent + @ensure.safe_insync?(:absent).should == true + end - it "should not mount itself if it is unmounted and should be defined" do - @provider.stubs(:ensure).returns(:unmounted) - @provider.stubs(:anything_mounted?).returns(false) + it "should be out of sync if it is :absent and should be :defined" do + @ensure.should = :defined + @ensure.safe_insync?(:absent).should == false + end - @ensure.stubs(:syncothers) - @provider.stubs(:create) - @provider.expects(:mount).never - @provider.expects(:unmount).never - @ensure.should = :present - @ensure.sync - end + it "should be out of sync if it is :absent and should be :mounted" do + @ensure.should = :mounted + @ensure.safe_insync?(:absent).should == false + end - it "should unmount itself if it is mounted and should be unmounted" do - @provider.stubs(:ensure).returns(:present) - @provider.stubs(:anything_mounted?).returns(true) + it "should be out of sync if it is :absent and should be :unmounted" do + @ensure.should = :unmounted + @ensure.safe_insync?(:absent).should == false + end - @ensure.stubs(:syncothers) - @provider.expects(:unmount) - @ensure.should = :unmounted - @ensure.sync - end + it "should be out of sync if it is :mounted and should be :absent" do + @ensure.should = :absent + @ensure.safe_insync?(:mounted).should == false + end - it "should ask the provider to mount itself" do - @provider.stubs(:ensure).returns(:present) - @ensure.stubs(:syncothers) - @provider.expects(:mount) - @ensure.should = :mounted - @ensure.sync - end + it "should be in sync if it is :mounted and should be :defined" do + @ensure.should = :defined + @ensure.safe_insync?(:mounted).should == true + end - it "should be insync if it is mounted and should be defined" do - @ensure.should = :defined - @ensure.safe_insync?(:mounted).should == true - end + it "should be in sync if it is :mounted and should be :mounted" do + @ensure.should = :mounted + @ensure.safe_insync?(:mounted).should == true + end - it "should be insync if it is unmounted and should be defined" do - @ensure.should = :defined - @ensure.safe_insync?(:unmounted).should == true - end + it "should be out in sync if it is :mounted and should be :unmounted" do + @ensure.should = :unmounted + @ensure.safe_insync?(:mounted).should == false + end - it "should be insync if it is mounted and should be present" do - @ensure.should = :present - @ensure.safe_insync?(:mounted).should == true - end - it "should be insync if it is unmounted and should be present" do - @ensure.should = :present - @ensure.safe_insync?(:unmounted).should == true - end - end + it "should be out of sync if it is :unmounted and should be :absent" do + @ensure.should = :absent + @ensure.safe_insync?(:unmounted).should == false + end + + it "should be in sync if it is :unmounted and should be :defined" do + @ensure.should = :defined + @ensure.safe_insync?(:unmounted).should == true + end + + it "should be out of sync if it is :unmounted and should be :mounted" do + @ensure.should = :mounted + @ensure.safe_insync?(:unmounted).should == false + end + + it "should be in sync if it is :unmounted and should be :unmounted" do + @ensure.should = :unmounted + @ensure.safe_insync?(:unmounted).should == true + end + + + it "should be out of sync if it is :ghost and should be :absent" do + @ensure.should = :absent + @ensure.safe_insync?(:ghost).should == false + end + + it "should be out of sync if it is :ghost and should be :defined" do + @ensure.should = :defined + @ensure.safe_insync?(:ghost).should == false + end + + it "should be out of sync if it is :ghost and should be :mounted" do + @ensure.should = :mounted + @ensure.safe_insync?(:ghost).should == false + end + + it "should be out of sync if it is :ghost and should be :unmounted" do + @ensure.should = :unmounted + @ensure.safe_insync?(:ghost).should == false + end + + end + + describe Puppet::Type.type(:mount), "when responding to events" do - describe "when responding to events" do it "should remount if it is currently mounted" do - @provider.expects(:anything_mounted?).returns(true) + @provider.expects(:mounted?).returns(true) @provider.expects(:remount) @mount.refresh end it "should not remount if it is not currently mounted" do - @provider.expects(:anything_mounted?).returns(false) + @provider.expects(:mounted?).returns(false) @provider.expects(:remount).never @mount.refresh end it "should not remount swap filesystems" do @mount[:fstype] = "swap" @provider.expects(:remount).never @mount.refresh end end end describe Puppet::Type.type(:mount), "when modifying an existing mount entry" do before do @provider = stub 'provider', :class => Puppet::Type.type(:mount).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :remount => nil Puppet::Type.type(:mount).defaultprovider.stubs(:new).returns(@provider) @mount = Puppet::Type.type(:mount).new(:name => "yay", :ensure => :mounted) {:device => "/foo/bar", :blockdevice => "/other/bar", :target => "/what/ever", :fstype => 'eh', :options => "", :pass => 0, :dump => 0, :atboot => 0, :ensure => :mounted}.each do |param, value| @mount.provider.stubs(param).returns value @mount[param] = value end - @mount.provider.stubs(:anything_mounted?).returns true - @mount.provider.stubs(:correctly_mounted?).returns true + @mount.provider.stubs(:mounted?).returns true # stub this to not try to create state.yaml Puppet::Util::Storage.stubs(:store) @catalog = Puppet::Resource::Catalog.new @catalog.add_resource @mount end it "should use the provider to change the dump value" do @mount.provider.expects(:dump).returns 0 @mount.provider.expects(:dump=).with(1) @mount[:dump] = 1 @catalog.apply end + + it "should flush changes before mounting" do + syncorder = sequence('syncorder') + @mount.provider.expects(:options).returns 'soft' + @mount.provider.expects(:ensure).returns :unmounted + @mount.provider.expects(:mounted?).returns false + + @mount.provider.expects(:options=).in_sequence(syncorder).with 'hard' + @mount.expects(:flush).in_sequence(syncorder) # Have to write with no options + @mount.provider.expects(:mount).in_sequence(syncorder) + @mount.expects(:flush).in_sequence(syncorder) # Call flush again cause we changed everything + + @mount[:ensure] = :mounted + @mount[:options] = 'hard' + + @catalog.apply + end + + it "should not flush before mounting if there are no other changes" do + syncorder = sequence('syncorder') + @mount.provider.expects(:ensure).returns :unmounted + @mount.provider.expects(:mounted?).returns false + @mount.provider.expects(:mount).in_sequence(syncorder) + @mount.expects(:flush).in_sequence(syncorder) # Call flush cause we changed everything + + @mount[:ensure] = :mounted + @catalog.apply + end + + it "should umount before flushing changes to disk" do + syncorder = sequence('syncorder') + @mount.provider.expects(:options).returns 'soft' + @mount.provider.expects(:ensure).returns :mounted + + @mount.provider.expects(:unmount).in_sequence(syncorder) + @mount.provider.expects(:options=).in_sequence(syncorder).with 'hard' + @mount.expects(:flush).in_sequence(syncorder) # Call inside syncothers + @mount.expects(:flush).in_sequence(syncorder) # I guess transaction or anything calls flush again + + @mount[:ensure] = :unmounted + @mount[:options] = 'hard' + + @catalog.apply + end + end diff --git a/test/data/providers/mount/parsed/aix.mount b/test/data/providers/mount/parsed/aix.mount new file mode 100644 index 000000000..380dbc5ae --- /dev/null +++ b/test/data/providers/mount/parsed/aix.mount @@ -0,0 +1,7 @@ +node mounted mounted over vfs date options +---- ------- ------------ --- ------------ ------------------- + /dev/hd0 / jfs Dec 17 08:04 rw, log =/dev/hd8 + /dev/hd3 /tmp jfs Dec 17 08:04 rw, log =/dev/hd8 + /dev/hd1 /home jfs Dec 17 08:06 rw, log =/dev/hd8 + /dev/hd2 /usr jfs Dec 17 08:06 rw, log =/dev/hd8 +sue /home/local/src /usr/code nfs Dec 17 08:06 ro, log =/dev/hd8 diff --git a/spec/fixtures/unit/provider/mount/mount-output.darwin.txt b/test/data/providers/mount/parsed/darwin.mount similarity index 84% rename from spec/fixtures/unit/provider/mount/mount-output.darwin.txt rename to test/data/providers/mount/parsed/darwin.mount index fbb9d9832..1bdfcf89a 100644 --- a/spec/fixtures/unit/provider/mount/mount-output.darwin.txt +++ b/test/data/providers/mount/parsed/darwin.mount @@ -1,5 +1,6 @@ /dev/disk0s2 on / (hfs, local, journaled) devfs on /dev (devfs, local, nobrowse) map -hosts on /net (autofs, nosuid, automounted, nobrowse) map auto_home on /home (autofs, automounted, nobrowse) /dev/disk0s3 on /usr (hfs, local, journaled) +/dev/fake on /ghost (hfs, local, journaled) diff --git a/spec/fixtures/unit/provider/mount/mount-output.hp-ux.txt b/test/data/providers/mount/parsed/hpux.mount similarity index 93% rename from spec/fixtures/unit/provider/mount/mount-output.hp-ux.txt rename to test/data/providers/mount/parsed/hpux.mount index 477926138..d414fa47a 100644 --- a/spec/fixtures/unit/provider/mount/mount-output.hp-ux.txt +++ b/test/data/providers/mount/parsed/hpux.mount @@ -1,16 +1,17 @@ / on rpool/ROOT/opensolaris read/write/setuid/devices/dev=2d90002 on Wed Dec 31 16:00:00 1969 /devices on /devices read/write/setuid/devices/dev=4a00000 on Thu Feb 17 14:34:02 2011 /dev on /dev read/write/setuid/devices/dev=4a40000 on Thu Feb 17 14:34:02 2011 /system/contract on ctfs read/write/setuid/devices/dev=4ac0001 on Thu Feb 17 14:34:02 2011 /proc on proc read/write/setuid/devices/dev=4b00000 on Thu Feb 17 14:34:02 2011 /etc/mnttab on mnttab read/write/setuid/devices/dev=4b40001 on Thu Feb 17 14:34:02 2011 /etc/svc/volatile on swap read/write/setuid/devices/xattr/dev=4b80001 on Thu Feb 17 14:34:02 2011 /system/object on objfs read/write/setuid/devices/dev=4bc0001 on Thu Feb 17 14:34:02 2011 /etc/dfs/sharetab on sharefs read/write/setuid/devices/dev=4c00001 on Thu Feb 17 14:34:02 2011 /lib/libc.so.1 on /usr/lib/libc/libc_hwcap1.so.1 read/write/setuid/devices/dev=2d90002 on Thu Feb 17 14:34:14 2011 /dev/fd on fd read/write/setuid/devices/dev=4d00001 on Thu Feb 17 14:34:18 2011 /tmp on swap read/write/setuid/devices/xattr/dev=4b80002 on Thu Feb 17 14:34:19 2011 /var/run on swap read/write/setuid/devices/xattr/dev=4b80003 on Thu Feb 17 14:34:19 2011 /export on rpool/export read/write/setuid/devices/nonbmand/exec/xattr/atime/dev=2d90006 on Thu Feb 17 14:37:48 2011 /export/home on rpool/export/home read/write/setuid/devices/nonbmand/exec/xattr/atime/dev=2d90007 on Thu Feb 17 14:37:48 2011 /rpool on rpool read/write/setuid/devices/nonbmand/exec/xattr/atime/dev=2d90009 on Thu Feb 17 14:37:48 2011 +/ghost on /dev/fake read/write/setuid/devices/nonbmand/exec/xattr/atime/dev=2d90009 on Thu Feb 17 14:37:48 2011 diff --git a/test/data/providers/mount/parsed/linux.mount b/test/data/providers/mount/parsed/linux.mount new file mode 100644 index 000000000..75dd71fd4 --- /dev/null +++ b/test/data/providers/mount/parsed/linux.mount @@ -0,0 +1,5 @@ +/dev/root on / type jfs (rw,noatime) +rc-svcdir on /lib64/rc/init.d type tmpfs (rw,nosuid,nodev,noexec,relatime,size=1024k,mode=755) +sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) +/dev/sda9 on /usr/portage type jfs (rw) +/dev/fake on /ghost type jfs (rw) diff --git a/test/data/providers/mount/parsed/solaris.mount b/test/data/providers/mount/parsed/solaris.mount new file mode 100644 index 000000000..26fabc575 --- /dev/null +++ b/test/data/providers/mount/parsed/solaris.mount @@ -0,0 +1,6 @@ +/ on /dev/dsk/c0t0d0s0 read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200000 on Mon Mar 18 08:48:45 2002 +/proc on /proc read/write/setuid/dev=4300000 on Mon Mar 18 08:48:44 2002 +/etc/mnttab on mnttab read/write/setuid/dev=43c0000 on Mon Mar 18 08:48:44 2002 +/tmp on swap read/write/setuid/xattr/dev=2 on Mon Mar 18 08:48:52 2002 +/export/home on /dev/dsk/c0t0d0s7 read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Mon Mar 18 +/ghost on /dev/dsk/c0t1d0s7 read/write/setuid/intr/largefiles/xattr/onerror=panic/dev=2200007 on Mon Mar 18 diff --git a/test/data/types/mount/linux.fstab b/test/data/types/mount/linux.fstab index b1debff9c..c94ec7fa8 100644 --- a/test/data/types/mount/linux.fstab +++ b/test/data/types/mount/linux.fstab @@ -1,11 +1,12 @@ # A sample fstab, typical for a Fedora system /dev/vg00/lv00 / ext3 defaults 1 1 LABEL=/boot /boot ext3 defaults 1 2 devpts /dev/pts devpts gid=5,mode=620 0 tmpfs /dev/shm tmpfs defaults 0 LABEL=/home /home ext3 defaults 1 2 /home /homes auto bind 0 2 proc /proc proc defaults 0 0 /dev/vg00/lv01 /spare ext3 defaults 1 2 sysfs /sys sysfs defaults 0 0 LABEL=SWAP-hda6 swap swap defaults 0 0 +/dev/sda1 /usr xfs noatime 0 0 diff --git a/test/data/types/mount/solaris.fstab b/test/data/types/mount/solaris.fstab index 54afc898c..348b9d50b 100644 --- a/test/data/types/mount/solaris.fstab +++ b/test/data/types/mount/solaris.fstab @@ -1,11 +1,12 @@ #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 - /dev/dsk/c0d0s0 /dev/rdsk/c0d0s0 / ufs 1 no - /dev/dsk/c0d0p0:boot - /boot pcfs - no - /devices - /devices devfs - no - ctfs - /system/contract ctfs - no - objfs - /system/object objfs - no - #swap - /tmp tmpfs - yes - +/dev/dsk/c0d0s2 /dev/rdsk/c0d0s2 /usr ufs 1 no -