diff --git a/lib/puppet/provider/nameservice/netinfo.rb b/lib/puppet/provider/nameservice/netinfo.rb index d7d6123e6..5d6c19969 100644 --- a/lib/puppet/provider/nameservice/netinfo.rb +++ b/lib/puppet/provider/nameservice/netinfo.rb @@ -1,208 +1,206 @@ # Manage NetInfo POSIX objects. Probably only used on OS X, but I suppose # it could be used elsewhere. require 'puppet' require 'puppet/provider/nameservice' class Puppet::Provider::NameService class NetInfo < Puppet::Provider::NameService # Attempt to flush the database, but this doesn't seem to work at all. def self.flush begin output = execute("/usr/sbin/lookupd -flushcache 2>&1") rescue Puppet::ExecutionFailure # Don't throw an error; it's just a failed cache flush Puppet.err "Could not flush lookupd cache: %s" % output end end # Similar to posixmethod, what key do we use to get data? Defaults # to being the object name. def self.netinfodir if defined? @netinfodir return @netinfodir else return @model.name.to_s + "s" end end def self.finish case self.name when :uid: noautogen when :gid: noautogen end end # How to add an object. def addcmd creatorcmd("-create") end def creatorcmd(arg) cmd = [command(:niutil)] cmd << arg cmd << "/" << "/%s/%s" % [self.class.netinfodir(), @model[:name]] return cmd.join(" ") end def deletecmd creatorcmd("-destroy") end def ensure=(arg) super # Because our stupid type can't create the whole thing at once, # we have to do this hackishness. Yay. if arg == :present # We need to generate the id if it's missing. @model.class.validstates.each do |name| next if name == :ensure - unless val = @model[name] + unless val = @model.should(name) if (@model.class.name == :user and name == :uid) or (@model.class.name == :group and name == :gid) val = autogen() else # No value, and it's not required, so skip it. info "No value for %s" % name next end end self.send(name.to_s + "=", val) end end end - #def exists? - # if self.report(:name) - # return true - # else - # return false - # end - #end - # Retrieve a specific value by name. def get(param) hash = getinfo(false) if hash return hash[param] else return :absent end end # Retrieve everything about this object at once, instead of separately. def getinfo(refresh = false) if refresh or (! defined? @infohash or ! @infohash) states = [:name] + self.class.model.validstates states.delete(:ensure) if states.include? :ensure @infohash = report(*states) end return @infohash end def modifycmd(param, value) cmd = [command(:niutil)] cmd << "-createprop" << "/" << "/%s/%s" % [self.class.netinfodir, @model[:name]] if key = netinfokey(param) cmd << key << "'%s'" % value else raise Puppet::DevError, "Could not find netinfokey for state %s" % self.class.name end cmd.join(" ") end # Determine the flag to pass to our command. def netinfokey(name) name = symbolize(name) self.class.option(name, :key) || name end # Retrieve the data, yo. # FIXME This should retrieve as much information as possible, # rather than retrieving it one at a time. def report(*params) dir = self.class.netinfodir() cmd = [command(:nireport), "/", "/%s" % dir] # We require the name in order to know if we match. There's no # way to just report on our individual object, we have to get the # whole list. params.unshift :name unless params.include? :name params.each do |param| if key = netinfokey(param) cmd << key.to_s else raise Puppet::DevError, "Could not find netinfokey for state %s" % self.class.name end end - self.debug "Executing %s" % cmd.join(" ").inspect - %x{#{cmd.join(" ")} 2>&1}.split("\n").each { |line| + begin + output = execute(cmd.join(" ")) + rescue Puppet::ExecutionFailure => detail + Puppet.err "Failed to call nireport: %s" % detail + return nil + end + + output.split("\n").each { |line| values = line.split(/\t/) hash = {} params.zip(values).each do |param, value| next if value == '#NoValue#' hash[param] = if value =~ /^[-0-9]+$/ Integer(value) else value end end if hash[:name] == @model[:name] return hash else next end # # if line =~ /^(\w+)\s+(.+)$/ # name = $1 # value = $2.sub(/\s+$/, '') # # if name == @model[:name] # if value =~ /^[-0-9]+$/ # return Integer(value) # else # return value # end # end } return nil end def retrieve raise "wtf?" @is = report() || :absent end def setuserlist(group, list) cmd = "#{command(:niutil)} -createprop / /groups/%s users %s" % [group, list.join(",")] begin output = execute(cmd) rescue Puppet::Execution::Failure => detail raise Puppet::Error, "Failed to set user list on %s: %s" % [group, detail] end end end end # $Id$ diff --git a/test/providers/group.rb b/test/providers/group.rb index 364d1750d..a23dbb50c 100755 --- a/test/providers/group.rb +++ b/test/providers/group.rb @@ -1,246 +1,246 @@ if __FILE__ == $0 $:.unshift '..' $:.unshift '../../lib' $puppetbase = "../.." end require 'etc' require 'puppet/type' require 'puppettest' require 'test/unit' class TestGroupProvider < Test::Unit::TestCase include TestPuppet def setup super @@tmpgroups = [] @provider = nil assert_nothing_raised { @provider = Puppet::Type.type(:group).defaultprovider } assert(@provider, "Could not find default group provider") assert(@provider.name != :fake, "Got a fake provider") end def teardown super Puppet.type(:group).clear @@tmpgroups.each { |group| unless missing?(group) remove(group) end } end def mkgroup(name, hash = {}) fakemodel = fakemodel(:group, name) group = nil assert_nothing_raised { group = @provider.new(fakemodel) } hash.each do |name, val| fakemodel[name] = val end assert(group, "Could not create provider group") return group end case Facter["operatingsystem"].value when "Darwin": def missing?(group) output = %x{nidump -r /groups/#{group} / 2>/dev/null}.chomp if output == "" return true else return false end assert_equal("", output, "Group %s is present:\n%s" % [group, output]) end def gid(name) %x{nireport / /groups name gid}.split("\n").each { |line| group, id = line.chomp.split(/\s+/) assert(id =~ /^-?\d+$/, "Group id %s for %s is not a number" % - [id, group]) + [id.inspect, group]) if group == name return Integer(id) end } return nil end def remove(group) system("niutil -destroy / /groups/%s" % group) end else def missing?(group) begin obj = Etc.getgrnam(group) return false rescue ArgumentError return true end end def gid(name) assert_nothing_raised { obj = Etc.getgrnam(name) return obj.gid } return nil end def remove(group) system("groupdel %s" % group) end end def groupnames %x{groups}.chomp.split(/ /) end def groupids Process.groups end def attrtest_ensure(group) old = group.ensure assert_nothing_raised { group.ensure = :absent } assert(!group.exists?, "Group was not deleted") assert_nothing_raised { group.ensure = :present } assert(group.exists?, "Group was not created") unless old == :present assert_nothing_raised { group.ensure = old } end end def attrtest_gid(group) old = gid(group.name) newgid = old while true newgid += 1 if newgid - old > 1000 $stderr.puts "Could not find extra test UID" return end begin Etc.getgrgid(newgid) rescue ArgumentError => detail break end end assert_nothing_raised("Failed to change group id") { group.gid = newgid } curgid = nil assert_nothing_raised { curgid = gid(group.name) } assert_equal(newgid, curgid, "GID was not changed") # Refresh group.getinfo(true) assert_equal(newgid, group.gid, "Object got wrong gid") assert_nothing_raised("Failed to change group id") { group.gid = old } end # Iterate over each of our groups and try to grab the gid. def test_ownprovidergroups groupnames().each { |group| gobj = nil comp = nil fakemodel = fakemodel(:group, group) assert_nothing_raised { gobj = @provider.new(fakemodel) } assert(gobj.gid, "Failed to retrieve gid") } end if Process.uid == 0 def test_mkgroup gobj = nil comp = nil name = "pptestgr" assert(missing?(name), "Group %s is still present" % name) group = mkgroup(name) @@tmpgroups << name assert(group.respond_to?(:addcmd), "no respondo?") assert_nothing_raised { group.create } assert(!missing?(name), "Group %s is missing" % name) tests = Puppet.type(:group).validstates tests.each { |test| if self.respond_to?("attrtest_%s" % test) self.send("attrtest_%s" % test, group) else $stderr.puts "Not testing attr %s of group" % test end } assert_nothing_raised { group.delete } end # groupadd -o is broken in FreeBSD. unless Facter["operatingsystem"].value == "FreeBSD" def test_duplicateIDs group1 = mkgroup("group1", :gid => 125) group2 = mkgroup("group2", :gid => 125) @@tmpgroups << "group1" @@tmpgroups << "group2" # Create the first group assert_nothing_raised { group1.create } # Not all OSes fail here, so we can't test that it doesn't work with # it off, only that it does work with it on. assert_nothing_raised { group2.model[:allowdupe] = :true } # Now create the second group assert_nothing_raised { group2.create } assert_equal(:present, group2.ensure, "Group did not get created") end end else $stderr.puts "Not running as root; skipping group creation tests." end end # $Id$ diff --git a/test/providers/user.rb b/test/providers/user.rb index e400ff3ad..0778a257f 100644 --- a/test/providers/user.rb +++ b/test/providers/user.rb @@ -1,528 +1,528 @@ if __FILE__ == $0 $:.unshift '..' $:.unshift '../../lib' $puppetbase = "../.." end require 'puppettest' require 'puppet' require 'test/unit' require 'facter' class TestUserProvider < Test::Unit::TestCase include FileTesting def setup super setme() @@tmpusers = [] @provider = nil assert_nothing_raised { @provider = Puppet::Type.type(:user).defaultprovider } assert(@provider, "Could not find default user provider") end def teardown @@tmpusers.each { |user| unless missing?(user) remove(user) end } super #Puppet.type(:user).clear end case Facter["operatingsystem"].value when "Darwin": def missing?(user) output = %x{nidump -r /users/#{user} / 2>/dev/null}.chomp if output == "" return true else return false end assert_equal("", output, "User %s is present:\n%s" % [user, output]) end def current?(param, user) state = Puppet.type(:user).states.find { |st| st.name == param } output = %x{nireport / /users name #{state.netinfokey}} output.split("\n").each { |line| if line =~ /^(\w+)\s+(.+)$/ username = $1 id = $2.sub(/\s+$/, '') if username == user.name if id =~ /^[-0-9]+$/ return Integer(id) else return id end end else raise "Could not match %s" % line end } return nil end def remove(user) system("niutil -destroy / /users/%s" % user) end else def missing?(user) begin obj = Etc.getpwnam(user) return false rescue ArgumentError return true end end def current?(param, user) state = Puppet.type(:user).states.find { |st| st.name == param } assert_nothing_raised { obj = Etc.getpwnam(user.name) return obj.send(user.posixmethod(param)) } return nil end def remove(user) system("userdel %s" % user) end end def eachstate Puppet::Type.type(:user).validstates.each do |state| yield state end end def findshell(old = nil) %w{/bin/sh /bin/bash /sbin/sh /bin/ksh /bin/zsh /bin/csh /bin/tcsh /usr/bin/sh /usr/bin/bash /usr/bin/ksh /usr/bin/zsh /usr/bin/csh /usr/bin/tcsh}.find { |shell| if old FileTest.exists?(shell) and shell != old else FileTest.exists?(shell) end } end def fakedata(name, param) case param when :name: name when :ensure: :present when :comment: "Puppet Testing User %s" % name - when :gid: Process.gid() + when :gid: nonrootgroup.name when :shell: findshell() when :home: "/home/%s" % name else return nil end end def mkuser(name) fakemodel = fakemodel(:user, name) user = nil assert_nothing_raised { user = @provider.new(fakemodel) } assert(user, "Could not create provider user") return user end def test_list names = nil assert_nothing_raised { names = @provider.listbyname } assert(names.length > 0, "Listed no users") # Now try it by object assert_nothing_raised { names = @provider.list } assert(names.length > 0, "Listed no users as objects") names.each do |obj| assert_instance_of(Puppet::Type.type(:user), obj) assert(obj[:provider], "Provider was not set") end end def test_infocollection fakemodel = fakemodel(:user, @me) user = nil assert_nothing_raised { user = @provider.new(fakemodel) } assert(user, "Could not create user provider") Puppet::Type.type(:user).validstates.each do |state| next if state == :ensure val = nil assert_nothing_raised { val = user.send(state) } assert(val != :absent, "State %s is missing" % state) assert(val, "Did not get value for %s" % state) end end def test_exists user = mkuser("nosuchuserok") assert(! user.exists?, "Fake user exists?") user = mkuser(@me) assert(user.exists?, "I don't exist?") end def attrtest_ensure(user) old = user.ensure assert_nothing_raised { user.ensure = :absent } assert(missing?(user.name), "User is still present") assert_nothing_raised { user.ensure = :present } assert(!missing?(user.name), "User is absent") assert_nothing_raised { user.ensure = :absent } unless old == :absent user.ensure = old end end def attrtest_comment(user) old = user.comment assert_nothing_raised { user.comment = "A different comment" } assert_equal("A different comment", current?(:comment, user), "Comment was not changed") assert_nothing_raised { user.comment = old } assert_equal(old, current?(:comment, user), "Comment was not reverted") end def attrtest_home(user) old = current?(:home, user) assert_nothing_raised { user.home = "/tmp" } assert_equal("/tmp", current?(:home, user), "Home was not changed") assert_nothing_raised { user.home = old } assert_equal(old, current?(:home, user), "Home was not reverted") end def attrtest_shell(user) old = current?(:shell, user) newshell = findshell(old) unless newshell $stderr.puts "Cannot find alternate shell; skipping shell test" return end assert_nothing_raised { user.shell = newshell } assert_equal(newshell, current?(:shell, user), "Shell was not changed") assert_nothing_raised { user.shell = old } assert_equal(old, current?(:shell, user), "Shell was not reverted") end def attrtest_gid(user) old = current?(:gid, user) newgroup = %w{nogroup nobody staff users daemon}.find { |gid| begin group = Etc.getgrnam(gid) rescue ArgumentError => detail next end old != group.gid } group = Etc.getgrnam(newgroup) unless newgroup $stderr.puts "Cannot find alternate group; skipping gid test" return end assert_raise(ArgumentError, "gid allowed a non-integer value") do user.gid = group.name end assert_nothing_raised("Failed to specify group by id") { user.gid = group.gid } assert_equal(group.gid, current?(:gid,user), "GID was not changed") assert_nothing_raised("Failed to change back to old gid") { user.gid = old } end def attrtest_uid(user) old = current?(:uid, user) newuid = old while true newuid += 1 if newuid - old > 1000 $stderr.puts "Could not find extra test UID" return end begin newuser = Etc.getpwuid(newuid) rescue ArgumentError => detail break end end assert_nothing_raised("Failed to change user id") { user.uid = newuid } assert_equal(newuid, current?(:uid, user), "UID was not changed") assert_nothing_raised("Failed to change user id") { user.uid = old } assert_equal(old, current?(:uid, user), "UID was not changed back") end def attrtest_groups(user) Etc.setgrent max = 0 while group = Etc.getgrent if group.gid > max and group.gid < 5000 max = group.gid end end groups = [] main = [] extra = [] 5.times do |i| i += 1 name = "pptstgr%s" % i tmpgroup = Puppet.type(:group).create( :name => name, :gid => max + i ) groups << tmpgroup cleanup do tmpgroup.provider.delete if tmpgroup.provider.exists? end if i < 3 main << name else extra << name end end # Create our test groups assert_apply(*groups) # Now add some of them to our user assert_nothing_raised { user.model[:groups] = extra.join(",") } # Some tests to verify that groups work correctly startig from nothing # Remove our user user.ensure = :absent # And add it again user.ensure = :present # Make sure that the group list is added at creation time. # This is necessary because we don't have default fakedata for groups. assert(user.groups, "Did not retrieve group list") list = user.groups.split(",") assert_equal(extra.sort, list.sort, "Group list was not set at creation time") # Now set to our main list of groups assert_nothing_raised { user.groups = main.join(",") } list = user.groups.split(",") assert_equal(main.sort, list.sort, "Group list is not equal") end if Process.uid == 0 def test_simpleuser name = "pptest" assert(missing?(name), "User %s is present" % name) user = mkuser(name) eachstate do |state| if val = fakedata(user.name, state) user.model[state] = val end end @@tmpusers << name assert_nothing_raised { user.create } assert_equal("Puppet Testing User pptest", user.comment, "Comment was not set") assert_nothing_raised { user.delete } assert(missing?(user.name), "User was not deleted") end def test_alluserstates user = nil name = "pptest" assert(missing?(name), "User %s is present" % name) user = mkuser(name) eachstate do |state| if val = fakedata(user.name, state) user.model[state] = val end end @@tmpusers << name assert_nothing_raised { user.create } assert_equal("Puppet Testing User pptest", user.comment, "Comment was not set") tests = Puppet::Type.type(:user).validstates just = nil tests.each { |test| next unless test == :groups if self.respond_to?("attrtest_%s" % test) self.send("attrtest_%s" % test, user) else Puppet.err "Not testing attr %s of user" % test end } assert_nothing_raised { user.delete } end # This is a weird method that shows how annoying the interface between # types and providers is. Grr. def test_duplicateIDs user1 = mkuser("user1") user1.create user1.uid = 125 user2 = mkuser("user2") user2.model[:uid] = 125 cleanup do user1.ensure = :absent user2.ensure = :absent end # Not all OSes fail here, so we can't test that it doesn't work with # it off, only that it does work with it on. assert_nothing_raised { user2.model[:allowdupe] = :true } assert_nothing_raised { user2.create } assert_equal(:present, user2.ensure, "User did not get created") end else $stderr.puts "Not root; skipping user creation/modification tests" end # Here is where we test individual providers def test_useradd_flags useradd = nil assert_nothing_raised { useradd = Puppet::Type.type(:user).provider(:useradd) } assert(useradd, "Did not retrieve useradd provider") user = nil assert_nothing_raised { fakemodel = fakemodel(:user, @me) user = useradd.new(fakemodel) } assert_equal("-d", user.send(:flag, :home), "Incorrect home flag") assert_equal("-s", user.send(:flag, :shell), "Incorrect shell flag") end end # $Id$