diff --git a/lib/puppet/suidmanager.rb b/lib/puppet/suidmanager.rb index a431e1456..4d00f59b0 100644 --- a/lib/puppet/suidmanager.rb +++ b/lib/puppet/suidmanager.rb @@ -1,96 +1,100 @@ require 'facter' module Puppet module SUIDManager platform = Facter["kernel"].value [:uid=, :gid=, :uid, :gid].each do |method| define_method(method) do |*args| # NOTE: 'method' is closed here. newmethod = method if platform == "Darwin" if !@darwinwarned Puppet.warning "Cannot change real UID on Darwin" @darwinwarned = true end newmethod = ("e" + method.to_s).intern end return Process.send(newmethod, *args) end module_function method end [:euid=, :euid, :egid=, :egid].each do |method| define_method(method) do |*args| Process.send(method, *args) end module_function method end def run_and_capture(command, new_uid=self.euid, new_gid=self.egid) output = nil asuser(new_uid, new_gid) do # capture both stdout and stderr unless we are on ruby < 1.8.4 # NOTE: this would be much better facilitated with a specialized popen() # (see the test suite for more details.) if (Facter['rubyversion'].value <=> "1.8.4") < 0 unless @@alreadywarned Puppet.warning "Cannot capture STDERR when running as another user on Ruby < 1.8.4" @@alreadywarned = true end output = %x{#{command}} else output = %x{#{command} 2>&1} end end [output, $?.dup] end module_function :run_and_capture def system(command, new_uid=self.euid, new_gid=self.egid) status = nil asuser(new_uid, new_gid) do Kernel.system(command) status = $?.dup end status end module_function :system def asuser(new_euid=nil, new_egid=nil) + # Unless we're root, don't do a damn thing. + unless Process.uid == 0 + return yield + end old_egid = old_euid = nil if new_egid saved_state_egid = new_egid new_egid = Puppet::Util.gid(new_egid) if new_egid == nil raise Puppet::Error, "Invalid group: %s" % saved_state_egid end old_egid = self.egid self.egid = new_egid end if new_euid saved_state_euid = new_euid new_euid = Puppet::Util.uid(new_euid) if new_euid == nil raise Puppet::Error, "Invalid user: %s" % saved_state_euid end old_euid = self.euid self.euid = new_euid end return yield ensure self.egid = old_egid if old_egid self.euid = old_euid if old_euid end module_function :asuser end end # $Id$ diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb index 5f8d6f31a..0141d1c3c 100644 --- a/lib/puppet/util.rb +++ b/lib/puppet/util.rb @@ -1,462 +1,462 @@ # A module to collect utility functions. require 'sync' require 'puppet/lock' module Puppet # A command failed to execute. - class ExecutionFailure < RuntimeError + class ExecutionFailure < Puppet::Error end module Util require 'benchmark' # Create a hash to store the different sync objects. @@syncresources = {} # Return the sync object associated with a given resource. def self.sync(resource) @@syncresources[resource] ||= Sync.new return @@syncresources[resource] end # Change the process to a different user def self.chuser if Facter["operatingsystem"].value == "Darwin" $stderr.puts "Ruby on darwin is broken; puppetmaster must run as root" return end if group = Puppet[:group] group = self.gid(group) unless group raise Puppet::Error, "No such group %s" % Puppet[:group] end unless Puppet::SUIDManager.gid == group begin Puppet::SUIDManager.egid = group Puppet::SUIDManager.gid = group rescue => detail Puppet.warning "could not change to group %s: %s" % [group.inspect, detail] $stderr.puts "could not change to group %s" % group.inspect # Don't exit on failed group changes, since it's # not fatal #exit(74) end end end if user = Puppet[:user] user = self.uid(user) unless user raise Puppet::Error, "No such user %s" % Puppet[:user] end unless Puppet::SUIDManager.uid == user begin Puppet::SUIDManager.uid = user Puppet::SUIDManager.euid = user rescue $stderr.puts "could not change to user %s" % user exit(74) end end end end # Create a shared lock for reading def self.readlock(file) self.sync(file).synchronize(Sync::SH) do File.open(file) { |f| f.lock_shared { |lf| yield lf } } end end # Create an exclusive lock for writing, and do the writing in a # tmp file. def self.writelock(file, mode = 0600) tmpfile = file + ".tmp" unless FileTest.directory?(File.dirname(tmpfile)) raise Puppet::DevError, "Cannot create %s; directory %s does not exist" % [file, File.dirname(file)] end self.sync(file).synchronize(Sync::EX) do File.open(file, "w", mode) do |rf| rf.lock_exclusive do |lrf| File.open(tmpfile, "w", mode) do |tf| yield tf end begin File.rename(tmpfile, file) rescue => detail Puppet.err "Could not rename %s to %s: %s" % [file, tmpfile, detail] end end end end end # Get the GID of a given group, provided either a GID or a name def self.gid(group) if group =~ /^\d+$/ group = Integer(group) end unless group raise Puppet::DevError, "Invalid group %s" % group.inspect end gid = nil obj = nil # We want to look the group up either way if group.is_a?(Integer) # If this doesn't find anything obj = Puppet.type(:group).find { |gobj| gobj.should(:gid) == group || gobj.is(:gid) == group } unless obj begin gobj = Etc.getgrgid(group) gid = gobj.gid rescue ArgumentError => detail # ignore it; we couldn't find the group end end else if obj = Puppet.type(:group)[group] obj[:check] = [:gid] else obj = Puppet.type(:group).create( :name => group, :check => [:gid] ) end obj.retrieve end if obj gid = obj.should(:gid) || obj.is(:gid) if gid == :absent gid = nil end end return gid end # Get the UID of a given user, whether a UID or name is provided def self.uid(user) uid = nil # if we don't have any user info, warn and GTFO. if !user Puppet.warning "Username provided for lookup is nil" return nil end if user =~ /^\d+$/ user = Integer(user) end if user.is_a?(Integer) # If this doesn't find anything obj = Puppet.type(:user).find { |uobj| uobj.should(:uid) == user || uobj.is(:uid) == user } unless obj begin uobj = Etc.getpwuid(user) uid = uobj.uid rescue ArgumentError => detail # ignore it; we couldn't find the user end end else unless obj = Puppet.type(:user)[user] obj = Puppet.type(:user).create( :name => user ) end obj[:check] = [:uid, :gid] end if obj obj.retrieve uid = obj.should(:uid) || obj.is(:uid) if uid == :absent uid = nil end end return uid end # Create instance methods for each of the log levels. This allows # the messages to be a little richer. Most classes will be calling this # method. def self.logmethods(klass, useself = true) Puppet::Log.eachlevel { |level| klass.send(:define_method, level, proc { |args| if args.is_a?(Array) args = args.join(" ") end if useself Puppet::Log.create( :level => level, :source => self, :message => args ) else Puppet::Log.create( :level => level, :message => args ) end }) } end # Proxy a bunch of methods to another object. def self.classproxy(klass, objmethod, *methods) classobj = class << klass; self; end methods.each do |method| classobj.send(:define_method, method) do |*args| obj = self.send(objmethod) obj.send(method, *args) end end end # Proxy a bunch of methods to another object. def self.proxy(klass, objmethod, *methods) methods.each do |method| klass.send(:define_method, method) do |*args| obj = self.send(objmethod) obj.send(method, *args) end end end # XXX this should all be done using puppet objects, not using # normal mkdir def self.recmkdir(dir,mode = 0755) if FileTest.exist?(dir) return false else tmp = dir.sub(/^\//,'') path = [File::SEPARATOR] tmp.split(File::SEPARATOR).each { |dir| path.push dir if ! FileTest.exist?(File.join(path)) Dir.mkdir(File.join(path), mode) elsif FileTest.directory?(File.join(path)) next else FileTest.exist?(File.join(path)) raise "Cannot create %s: basedir %s is a file" % [dir, File.join(path)] end } return true end end # Execute a given chunk of code with a new umask. def self.withumask(mask) cur = File.umask(mask) begin yield ensure File.umask(cur) end end def benchmark(*args) msg = args.pop level = args.pop object = nil if args.empty? object = Puppet else object = args.pop end unless level puts caller.join("\n") raise Puppet::DevError, "Failed to provide level" end unless object.respond_to? level raise Puppet::DevError, "Benchmarked object does not respond to %s" % level end # Only benchmark if our log level is high enough if level != :none and Puppet::Log.sendlevel?(level) result = nil seconds = Benchmark.realtime { yield } object.send(level, msg + (" in %0.2f seconds" % seconds)) return seconds else yield end end def binary(bin) if bin =~ /^\// if FileTest.exists? bin return true else return nil end else ENV['PATH'].split(":").each do |dir| if FileTest.exists? File.join(dir, bin) return File.join(dir, bin) end end return nil end end module_function :binary # Execute the provided command in a pipe, yielding the pipe object. def execpipe(command, failonfail = true) if respond_to? :debug debug "Executing '%s'" % command else Puppet.debug "Executing '%s'" % command end output = open("| #{command} 2>&1") do |pipe| yield pipe end if failonfail unless $? == 0 raise ExecutionFailure, output end end return output end def execfail(command, exception) begin output = execute(command) return output rescue ExecutionFailure raise exception, output end end # Execute the desired command, and return the status and output. def execute(command, failonfail = true) if respond_to? :debug debug "Executing '%s'" % command else Puppet.debug "Executing '%s'" % command end command += " 2>&1" unless command =~ />/ output = %x{#{command}} if failonfail unless $? == 0 raise ExecutionFailure, "Could not execute '%s': %s" % [command, output] end end return output end module_function :execute # Create an exclusive lock. def threadlock(resource, type = Sync::EX) Puppet::Util.sync(resource).synchronize(type) do yield end end # Because some modules provide their own version of this method. alias util_execute execute module_function :benchmark def memory unless defined? @pmap pmap = %x{which pmap 2>/dev/null}.chomp if $? != 0 or pmap =~ /^no/ @pmap = nil else @pmap = pmap end end if @pmap return %x{pmap #{Process.pid}| grep total}.chomp.sub(/^\s*total\s+/, '').sub(/K$/, '').to_i else 0 end end def symbolize(value) if value.respond_to? :intern return value.intern else value end end def symbolizehash(hash) newhash = {} hash.each do |name, val| if name.is_a? String newhash[name.intern] = val else newhash[name] = val end end end def symbolizehash!(hash) hash.each do |name, val| if name.is_a? String hash[name.intern] = val hash.delete(name) end end return hash end module_function :symbolize, :symbolizehash, :symbolizehash! # Just benchmark, with no logging. def thinmark seconds = Benchmark.realtime { yield } return seconds end module_function :memory end end require 'puppet/util/methodhelper' require 'puppet/util/metaid' require 'puppet/util/classgen' require 'puppet/util/docs' require 'puppet/util/execution' require 'puppet/util/package' require 'puppet/util/warnings' # $Id$ diff --git a/test/lib/puppettest.rb b/test/lib/puppettest.rb index e8b328de3..e7ee99c35 100644 --- a/test/lib/puppettest.rb +++ b/test/lib/puppettest.rb @@ -1,208 +1,212 @@ require 'puppet' require 'test/unit' module PuppetTest # Find the root of the Puppet tree; this is not the test directory, but # the parent of that dir. def basedir unless defined? @@basedir case $0 when /rake_test_loader/ @@basedir = File.dirname(Dir.getwd) else dir = nil if /^#{File::SEPARATOR}.+\.rb/ dir = $0 else dir = File.join(Dir.getwd, $0) end 3.times { dir = File.dirname(dir) } @@basedir = dir end end @@basedir end def cleanup(&block) @@cleaners << block end def datadir File.join(basedir, "test", "data") end def exampledir(*args) unless defined? @@exampledir @@exampledir = File.join(basedir, "examples") end if args.empty? return @@exampledir else return File.join(@@exampledir, *args) end end module_function :basedir, :datadir, :exampledir def rake? $0 =~ /rake_test_loader/ end def setup @memoryatstart = Puppet::Util.memory if defined? @@testcount @@testcount += 1 else @@testcount = 0 end @configpath = File.join(tmpdir, self.class.to_s + "configdir" + @@testcount.to_s + "/" ) unless defined? $user and $group $user = nonrootuser().uid.to_s $group = nonrootgroup().gid.to_s end Puppet[:user] = $user Puppet[:group] = $group Puppet[:confdir] = @configpath Puppet[:vardir] = @configpath unless File.exists?(@configpath) Dir.mkdir(@configpath) end @@tmpfiles = [@configpath, tmpdir()] @@tmppids = [] @@cleaners = [] # If we're running under rake, then disable debugging and such. if rake? and ! Puppet[:debug] Puppet::Log.close Puppet::Log.newdestination tempfile() Puppet[:httplog] = tempfile() else Puppet::Log.newdestination :console Puppet::Log.level = :debug #$VERBOSE = 1 Puppet.info @method_name end #if $0 =~ /.+\.rb/ or Puppet[:debug] # Puppet::Log.newdestination :console # Puppet::Log.level = :debug # #$VERBOSE = 1 # Puppet.info @method_name #else # Puppet::Log.close # Puppet::Log.newdestination tempfile() # Puppet[:httplog] = tempfile() #end Puppet[:ignoreschedules] = true Puppet[:trace] = true end def tempfile if defined? @@tmpfilenum @@tmpfilenum += 1 else @@tmpfilenum = 1 end f = File.join(self.tmpdir(), self.class.to_s + "_" + @method_name + @@tmpfilenum.to_s) @@tmpfiles << f return f end def tstdir dir = tempfile() Dir.mkdir(dir) return dir end def tmpdir unless defined? @tmpdir and @tmpdir @tmpdir = case Facter["operatingsystem"].value when "Darwin": "/private/tmp" when "SunOS": "/var/tmp" else "/tmp" end @tmpdir = File.join(@tmpdir, "puppettesting") unless File.exists?(@tmpdir) FileUtils.mkdir_p(@tmpdir) File.chmod(01777, @tmpdir) end end @tmpdir end def teardown stopservices @@cleaners.each { |cleaner| cleaner.call() } @@tmpfiles.each { |file| + unless file =~ /tmp/ + puts "Not deleting tmpfile %s" % file + next + end if FileTest.exists?(file) system("chmod -R 755 %s" % file) system("rm -rf %s" % file) end } @@tmpfiles.clear @@tmppids.each { |pid| %x{kill -INT #{pid} 2>/dev/null} } @@tmppids.clear Puppet::Type.allclear Puppet::Storage.clear Puppet::Rails.clear Puppet.clear @memoryatend = Puppet::Util.memory diff = @memoryatend - @memoryatstart if diff > 1000 Puppet.info "%s#%s memory growth (%s to %s): %s" % [self.class, @method_name, @memoryatstart, @memoryatend, diff] end # reset all of the logs Puppet::Log.close # Just in case there are processes waiting to die... require 'timeout' begin Timeout::timeout(5) do Process.waitall end rescue Timeout::Error # just move on end if File.stat("/dev/null").mode & 007777 != 0666 File.open("/tmp/nullfailure", "w") { |f| f.puts self.class } exit(74) end end end require 'puppettest/support' require 'puppettest/filetesting' require 'puppettest/fakes' require 'puppettest/exetest' require 'puppettest/parsertesting' require 'puppettest/servertest' # $Id$ diff --git a/test/other/storage.rb b/test/other/storage.rb index 789495fee..2b8dca5e6 100755 --- a/test/other/storage.rb +++ b/test/other/storage.rb @@ -1,93 +1,99 @@ require 'puppet' require 'puppettest' class TestStorage < Test::Unit::TestCase include PuppetTest def mkfile path = tempfile() File.open(path, "w") { |f| f.puts :yayness } f = Puppet.type(:file).create( :name => path, :check => %w{checksum type} ) return f end def test_storeandretrieve path = tempfile() f = mkfile() + # Load first, since that's what we do in the code base; this creates + # all of the necessary directories. + assert_nothing_raised { + Puppet::Storage.load + } + hash = {:a => :b, :c => :d} state = nil assert_nothing_raised { state = Puppet::Storage.cache(f) } assert(!state.include?("name")) assert_nothing_raised { state["name"] = hash } assert_nothing_raised { Puppet::Storage.store } assert_nothing_raised { Puppet::Storage.clear } assert_nothing_raised { Puppet::Storage.load } # Reset it state = nil assert_nothing_raised { state = Puppet::Storage.cache(f) } assert_equal(state["name"], hash) end # we're getting corrupt files, probably because multiple processes # are reading or writing the file at once # so we need to test that def test_multiwrite f = mkfile() value = {:a => :b} threads = [] 9.times { |a| threads << Thread.new { 9.times { |b| assert_nothing_raised { Puppet::Storage.load state = Puppet::Storage.cache(f) value.each { |k,v| state[k] = v } state[:e] = rand(100) Puppet::Storage.store } } } } threads.each { |th| th.join } end def test_emptyrestore Puppet::Storage.load Puppet::Storage.store Puppet::Storage.clear Puppet::Storage.load f = mkfile() state = Puppet::Storage.cache(f) assert_same Hash, state.class assert_equal 0, state.size end end # $Id$ diff --git a/test/types/filesources.rb b/test/types/filesources.rb index f121586c3..bdabcf33a 100755 --- a/test/types/filesources.rb +++ b/test/types/filesources.rb @@ -1,677 +1,671 @@ require 'puppet' require 'cgi' require 'fileutils' require 'puppettest' class TestFileSources < Test::Unit::TestCase include PuppetTest::FileTesting def setup super begin initstorage rescue system("rm -rf %s" % Puppet[:statefile]) end if defined? @port @port += 1 else @port = 8800 end end def initstorage Puppet::Storage.init Puppet::Storage.load end def clearstorage Puppet::Storage.store Puppet::Storage.clear end def test_newchild path = tempfile() @@tmpfiles.push path FileUtils.mkdir_p path File.open(File.join(path,"childtest"), "w") { |of| of.puts "yayness" } file = nil comp = nil trans = nil assert_nothing_raised { file = Puppet.type(:file).create( :name => path ) } child = nil assert_nothing_raised { child = file.newchild("childtest", true) } assert(child) assert_raise(Puppet::DevError) { file.newchild(File.join(path,"childtest"), true) } end def test_simplelocalsource path = tempfile() @@tmpfiles.push path FileUtils.mkdir_p path frompath = File.join(path,"source") topath = File.join(path,"dest") fromfile = nil tofile = nil trans = nil File.open(frompath, File::WRONLY|File::CREAT|File::APPEND) { |of| of.puts "yayness" } assert_nothing_raised { tofile = Puppet.type(:file).create( :name => topath, :source => frompath ) } assert_apply(tofile) assert(FileTest.exists?(topath), "File #{topath} is missing") from = File.open(frompath) { |o| o.read } to = File.open(topath) { |o| o.read } assert_equal(from,to) @@tmpfiles.push path end def recursive_source_test(fromdir, todir) Puppet::Type.allclear initstorage tofile = nil trans = nil assert_nothing_raised { tofile = Puppet.type(:file).create( :path => todir, :recurse => true, :backup => false, :source => fromdir ) } assert_apply(tofile) assert(FileTest.exists?(todir), "Created dir %s does not exist" % todir) Puppet::Type.allclear end def run_complex_sources(networked = false) path = tempfile() @@tmpfiles.push path # first create the source directory FileUtils.mkdir_p path # okay, let's create a directory structure fromdir = File.join(path,"fromdir") Dir.mkdir(fromdir) FileUtils.cd(fromdir) { mkranddirsandfiles() } todir = File.join(path, "todir") source = fromdir if networked source = "puppet://localhost/%s%s" % [networked, fromdir] end recursive_source_test(source, todir) return [fromdir,todir] end def test_complex_sources_twice fromdir, todir = run_complex_sources assert_trees_equal(fromdir,todir) recursive_source_test(fromdir, todir) assert_trees_equal(fromdir,todir) end def test_sources_with_deleted_destfiles fromdir, todir = run_complex_sources # then delete some files assert(FileTest.exists?(todir)) missing_files = delete_random_files(todir) # and run recursive_source_test(fromdir, todir) missing_files.each { |file| assert(FileTest.exists?(file), "Deleted file %s is still missing" % file) } # and make sure they're still equal assert_trees_equal(fromdir,todir) end def test_sources_with_readonly_destfiles fromdir, todir = run_complex_sources assert(FileTest.exists?(todir)) readonly_random_files(todir) recursive_source_test(fromdir, todir) # and make sure they're still equal assert_trees_equal(fromdir,todir) end def test_sources_with_modified_dest_files fromdir, todir = run_complex_sources assert(FileTest.exists?(todir)) # then modify some files modify_random_files(todir) recursive_source_test(fromdir, todir) # and make sure they're still equal assert_trees_equal(fromdir,todir) end def test_sources_with_added_destfiles fromdir, todir = run_complex_sources assert(FileTest.exists?(todir)) # and finally, add some new files add_random_files(todir) recursive_source_test(fromdir, todir) fromtree = file_list(fromdir) totree = file_list(todir) assert(fromtree != totree, "Trees are incorrectly equal") # then remove our new files FileUtils.cd(todir) { %x{find . 2>/dev/null}.chomp.split(/\n/).each { |file| if file =~ /file[0-9]+/ File.unlink(file) end } } # and make sure they're still equal assert_trees_equal(fromdir,todir) end def test_RecursionWithAddedFiles basedir = tempfile() Dir.mkdir(basedir) @@tmpfiles << basedir file1 = File.join(basedir, "file1") file2 = File.join(basedir, "file2") subdir1 = File.join(basedir, "subdir1") file3 = File.join(subdir1, "file") File.open(file1, "w") { |f| 3.times { f.print rand(100) } } rootobj = nil assert_nothing_raised { rootobj = Puppet.type(:file).create( :name => basedir, :recurse => true, :check => %w{type owner} ) rootobj.evaluate } klass = Puppet.type(:file) assert(klass[basedir]) assert(klass[file1]) assert_nil(klass[file2]) File.open(file2, "w") { |f| 3.times { f.print rand(100) } } assert_nothing_raised { rootobj.evaluate } assert(klass[file2]) Dir.mkdir(subdir1) File.open(file3, "w") { |f| 3.times { f.print rand(100) } } assert_nothing_raised { rootobj.evaluate } assert(klass[file3]) end def mkfileserverconf(mounts) file = tempfile() File.open(file, "w") { |f| mounts.each { |path, name| f.puts "[#{name}]\n\tpath #{path}\n\tallow *\n" } } @@tmpfiles << file return file end def test_NetworkSources server = nil - basedir = tempfile() - @@tmpfiles << basedir - Dir.mkdir(basedir) - mounts = { "/" => "root" } fileserverconf = mkfileserverconf(mounts) - Puppet[:confdir] = basedir - Puppet[:vardir] = basedir Puppet[:autosign] = true Puppet[:masterport] = 8762 serverpid = nil assert_nothing_raised() { server = Puppet::Server.new( :Handlers => { :CA => {}, # so that certs autogenerate :FileServer => { :Config => fileserverconf } } ) } serverpid = fork { assert_nothing_raised() { #trap(:INT) { server.shutdown; Kernel.exit! } trap(:INT) { server.shutdown } server.start } } @@tmppids << serverpid sleep(1) fromdir, todir = run_complex_sources("root") assert_trees_equal(fromdir,todir) recursive_source_test(fromdir, todir) assert_trees_equal(fromdir,todir) assert_nothing_raised { system("kill -INT %s" % serverpid) } end def test_networkSourcesWithoutService server = nil Puppet[:autosign] = true Puppet[:masterport] = 8765 serverpid = nil assert_nothing_raised() { server = Puppet::Server.new( :Handlers => { :CA => {}, # so that certs autogenerate } ) } serverpid = fork { assert_nothing_raised() { #trap(:INT) { server.shutdown; Kernel.exit! } trap(:INT) { server.shutdown } server.start } } @@tmppids << serverpid sleep(1) name = File.join(tmpdir(), "nosourcefile") file = Puppet.type(:file).create( :source => "puppet://localhost/dist/file", :name => name ) assert_nothing_raised { file.retrieve } comp = newcomp("nosource", file) assert_nothing_raised { comp.evaluate } assert(!FileTest.exists?(name), "File with no source exists anyway") end def test_unmountedNetworkSources server = nil mounts = { "/" => "root", "/noexistokay" => "noexist" } fileserverconf = mkfileserverconf(mounts) Puppet[:autosign] = true Puppet[:masterport] = @port serverpid = nil assert_nothing_raised() { server = Puppet::Server.new( :Port => @port, :Handlers => { :CA => {}, # so that certs autogenerate :FileServer => { :Config => fileserverconf } } ) } serverpid = fork { assert_nothing_raised() { #trap(:INT) { server.shutdown; Kernel.exit! } trap(:INT) { server.shutdown } server.start } } @@tmppids << serverpid sleep(1) name = File.join(tmpdir(), "nosourcefile") file = Puppet.type(:file).create( :source => "puppet://localhost/noexist/file", :name => name ) assert_nothing_raised { file.retrieve } comp = newcomp("nosource", file) assert_nothing_raised { comp.evaluate } assert(!FileTest.exists?(name), "File with no source exists anyway") end def test_alwayschecksum from = tempfile() to = tempfile() File.open(from, "w") { |f| f.puts "yayness" } File.open(to, "w") { |f| f.puts "yayness" } file = nil # Now the files should be exactly the same, so we should not see attempts # at copying assert_nothing_raised { file = Puppet.type(:file).create( :path => to, :source => from ) } file.retrieve assert(file.is(:checksum), "File does not have a checksum state") assert_equal(0, file.evaluate.length, "File produced changes") end def test_sourcepaths files = [] 3.times { files << tempfile() } to = tempfile() File.open(files[-1], "w") { |f| f.puts "yee-haw" } file = nil assert_nothing_raised { file = Puppet.type(:file).create( :name => to, :source => files ) } comp = newcomp(file) assert_events([:file_created], comp) assert(File.exists?(to), "File does not exist") txt = nil File.open(to) { |f| txt = f.read.chomp } assert_equal("yee-haw", txt, "Contents do not match") end # Make sure that source-copying updates the checksum on the same run def test_checksumchange source = tempfile() dest = tempfile() File.open(dest, "w") { |f| f.puts "boo" } File.open(source, "w") { |f| f.puts "yay" } file = nil assert_nothing_raised { file = Puppet.type(:file).create( :name => dest, :source => source ) } file.retrieve assert_events([:file_changed], file) file.retrieve assert_events([], file) end # Make sure that source-copying updates the checksum on the same run def test_sourcebeatsensure source = tempfile() dest = tempfile() File.open(source, "w") { |f| f.puts "yay" } file = nil assert_nothing_raised { file = Puppet.type(:file).create( :name => dest, :ensure => "file", :source => source ) } file.retrieve assert_events([:file_created], file) file.retrieve assert_events([], file) assert_events([], file) end def test_sourcewithlinks source = tempfile() link = tempfile() dest = tempfile() File.open(source, "w") { |f| f.puts "yay" } File.symlink(source, link) file = nil assert_nothing_raised { file = Puppet.type(:file).create( :name => dest, :source => link ) } # Default to skipping links assert_events([], file) assert(! FileTest.exists?(dest), "Created link") # Now follow the links file[:links] = :follow assert_events([:file_created], file) assert(FileTest.file?(dest), "Destination is not a file") # Now copy the links #assert_raise(Puppet::FileServerError) { trans = nil assert_nothing_raised { file[:links] = :manage comp = newcomp(file) trans = comp.evaluate trans.evaluate } assert(trans.failed?(file), "Object did not fail to copy links") end def test_changes source = tempfile() dest = tempfile() File.open(source, "w") { |f| f.puts "yay" } obj = nil assert_nothing_raised { obj = Puppet.type(:file).create( :name => dest, :source => source ) } assert_events([:file_created], obj) assert_equal(File.read(source), File.read(dest), "Files are not equal") assert_events([], obj) File.open(source, "w") { |f| f.puts "boo" } assert_events([:file_changed], obj) assert_equal(File.read(source), File.read(dest), "Files are not equal") assert_events([], obj) File.open(dest, "w") { |f| f.puts "kaboom" } # There are two changes, because first the checksum is noticed, and # then the source causes a change assert_events([:file_changed, :file_changed], obj) assert_equal(File.read(source), File.read(dest), "Files are not equal") assert_events([], obj) end def test_file_source_with_space dir = tempfile() source = File.join(dir, "file with spaces") Dir.mkdir(dir) File.open(source, "w") { |f| f.puts "yayness" } newdir = tempfile() newpath = File.join(newdir, "file with spaces") file = Puppet::Type.newfile( :path => newdir, :source => dir, :recurse => true ) assert_apply(file) assert(FileTest.exists?(newpath), "Did not create file") assert_equal("yayness\n", File.read(newpath)) end # Make sure files aren't replaced when replace is false, but otherwise # are. def test_replace source = tempfile() File.open(source, "w") { |f| f.puts "yayness" } dest = tempfile() file = Puppet::Type.newfile( :path => dest, :source => source, :recurse => true ) assert_apply(file) assert(FileTest.exists?(dest), "Did not create file") assert_equal("yayness\n", File.read(dest)) # Now set :replace assert_nothing_raised { file[:replace] = false } File.open(source, "w") { |f| f.puts "funtest" } assert_apply(file) # Make sure it doesn't change. assert_equal("yayness\n", File.read(dest)) # Now set it to true and make sure it does change. assert_nothing_raised { file[:replace] = true } assert_apply(file) # Make sure it doesn't change. assert_equal("funtest\n", File.read(dest)) end # Testing #285. This just makes sure that URI parsing works correctly. def test_fileswithpoundsigns dir = tstdir() subdir = File.join(dir, "#dir") Dir.mkdir(subdir) file = File.join(subdir, "file") File.open(file, "w") { |f| f.puts "yayness" } dest = tempfile() source = "file://localhost#{dir}" obj = Puppet::Type.newfile( :path => dest, :source => source, :recurse => true ) newfile = File.join(dest, "#dir", "file") poundsource = "file://localhost#{subdir}" sourceobj = path = nil assert_nothing_raised { sourceobj, path = obj.uri2obj(poundsource) } assert_equal("/localhost" + URI.escape(subdir), path) assert_apply(obj) assert(FileTest.exists?(newfile), "File did not get created") assert_equal("yayness\n", File.read(newfile)) end end # $Id$ diff --git a/test/types/package.rb b/test/types/package.rb index e290d12ad..a257a9324 100644 --- a/test/types/package.rb +++ b/test/types/package.rb @@ -1,488 +1,488 @@ require 'puppettest' require 'puppet' require 'facter' $platform = Facter["operatingsystem"].value unless Puppet.type(:package).defaultprovider puts "No default package type for %s; skipping package tests" % $platform else class TestPackages < Test::Unit::TestCase include PuppetTest::FileTesting def setup super #@list = Puppet.type(:package).getpkglist Puppet.type(:package).clear end # These are packages that we're sure will be installed def installedpkgs pkgs = nil case $platform when "SunOS" pkgs = %w{SMCossh} when "Debian": pkgs = %w{ssh openssl} when "Fedora": pkgs = %w{openssh} when "OpenBSD": pkgs = %w{vim} when "FreeBSD": pkgs = %w{sudo} when "Darwin": pkgs = %w{gettext} else Puppet.notice "No test package for %s" % $platform return [] end return pkgs end def modpkg(pkg) case $platform when "Solaris": pkg[:adminfile] = "/usr/local/pkg/admin_file" end end def mkpkgs(list = nil, useensure = true) list ||= tstpkgs() list.each { |pkg, source| hash = {:name => pkg} if useensure hash[:ensure] = "installed" end if source source = source[0] if source.is_a? Array hash[:source] = source end # Override the default package type for our test packages. if Facter["operatingsystem"].value == "Darwin" hash[:provider] = "darwinport" end obj = Puppet.type(:package).create(hash) assert(pkg, "Could not create package") modpkg(obj) yield obj } end def tstpkgs retval = [] case $platform when "Solaris": arch = Facter["hardwareisa"].value + Facter["operatingsystemrelease"].value case arch when "i3865.10": retval = {"SMCrdesk" => [ "/usr/local/pkg/rdesktop-1.3.1-sol10-intel-local", "/usr/local/pkg/rdesktop-1.4.1-sol10-x86-local" ]} when "sparc5.8": retval = {"SMCarc" => "/usr/local/pkg/arc-5.21e-sol8-sparc-local"} when "i3865.8": retval = {"SMCarc" => "/usr/local/pkg/arc-5.21e-sol8-intel-local"} end when "OpenBSD": retval = {"aalib" => "ftp://ftp.usa.openbsd.org/pub/OpenBSD/3.8/packages/i386/aalib-1.2-no_x11.tgz"} when "Debian": retval = {"zec" => nil} #when "RedHat": type = :rpm when "Fedora": retval = {"wv" => nil} when "CentOS": retval = {"enhost" => [ "/home/luke/rpm/RPMS/noarch/enhost-1.0.1-1.noarch.rpm", "/home/luke/rpm/RPMS/noarch/enhost-1.0.2-1.noarch.rpm" ]} when "Darwin": retval = {"aop" => nil} when "FreeBSD": retval = {"yahtzee" => nil} when "RedHat": retval = {"puppet" => "/home/luke/rpm/RPMS/i386/puppet-0.16.1-1.i386.rpm"} else Puppet.notice "No test packages for %s" % $platform end return retval end def mkpkgcomp(pkg) assert_nothing_raised { pkg = Puppet.type(:package).create(:name => pkg, :ensure => "present") } assert_nothing_raised { pkg.retrieve } comp = newcomp("package", pkg) return comp end def test_retrievepkg mkpkgs(installedpkgs()) { |obj| assert(obj, "could not create package") assert_nothing_raised { obj.retrieve } assert_instance_of(String, obj[:ensure], "Ensure did not return a version number") assert(obj[:ensure] =~ /[0-9.]/, "Ensure did not return a version number") } end + def test_latestpkg + mkpkgs { |pkg| + next unless pkg.respond_to? :latest + assert_nothing_raised { + assert(pkg.latest, + "Package %s did not return value for 'latest'" % pkg.name) + } + } + end + + # Make sure our package type supports listing. + def test_listing + pkgtype = Puppet::Type.type(:package) + + assert_nothing_raised("Could not list packages") do + count = 0 + pkgtype.list.each do |pkg| + assert_instance_of(Puppet::Type.type(:package), pkg) + count += 1 + end + + assert(count > 1, "Did not get any packages") + end + end + + unless Puppet::SUIDManager.uid == 0 + $stderr.puts "Run as root to perform package installation tests" + else def test_nosuchpkg obj = nil assert_nothing_raised { obj = Puppet.type(:package).create( :name => "thispackagedoesnotexist", :ensure => :installed ) } assert(obj, "Failed to create fake package") assert_nothing_raised { obj.retrieve } assert_equal(:absent, obj.is(:ensure), "Somehow retrieved unknown pkg's version") state = obj.state(:ensure) assert(state, "Could not retrieve ensure state") # Add a fake state, for those that need it file = tempfile() File.open(file, "w") { |f| f.puts :yayness } obj[:source] = file - assert_raise(Puppet::Error, + assert_raise(Puppet::Error, Puppet::ExecutionFailure, "Successfully installed nonexistent package") { state.sync } end - def test_latestpkg - mkpkgs { |pkg| - next unless pkg.respond_to? :latest - assert_nothing_raised { - assert(pkg.latest, - "Package %s did not return value for 'latest'" % pkg.name) - } - } - end - - # Make sure our package type supports listing. - def test_listing - pkgtype = Puppet::Type.type(:package) - - assert_nothing_raised("Could not list packages") do - count = 0 - pkgtype.list.each do |pkg| - assert_instance_of(Puppet::Type.type(:package), pkg) - count += 1 - end - - assert(count > 1, "Did not get any packages") - end - end - - unless Puppet::SUIDManager.uid == 0 - $stderr.puts "Run as root to perform package installation tests" - else def test_installpkg mkpkgs { |pkg| # we first set install to 'true', and make sure something gets # installed assert_nothing_raised { pkg.retrieve } if hash = pkg.provider.query and hash[:ensure] != :absent Puppet.notice "Test package %s is already installed; please choose a different package for testing" % pkg next end comp = newcomp("package", pkg) assert_events([:package_installed], comp, "package") pkg.retrieve assert(pkg.insync?, "Package is not in sync") # then uninstall it assert_nothing_raised { pkg[:ensure] = "absent" } pkg.retrieve assert(! pkg.insync?, "Package is in sync") assert_events([:package_removed], comp, "package") # and now set install to 'latest' and verify it installs if pkg.respond_to?(:latest) assert_nothing_raised { pkg[:ensure] = "latest" } assert_events([:package_installed], comp, "package") pkg.retrieve assert(pkg.insync?, "After install, package is not insync") assert_nothing_raised { pkg[:ensure] = "absent" } pkg.retrieve assert(! pkg.insync?, "Package is insync") assert_events([:package_removed], comp, "package") end } end # Make sure that a default is used for 'ensure' def test_ensuredefault # Tell mkpkgs not to set 'ensure'. mkpkgs(nil, false) { |pkg| assert_nothing_raised { pkg.retrieve } assert(!pkg.insync?, "Package thinks it's in sync") assert_apply(pkg) pkg.retrieve assert(pkg.insync?, "Package does not think it's in sync") pkg[:ensure] = :absent assert_apply(pkg) } end def test_upgradepkg tstpkgs.each do |name, sources| unless sources and sources.is_a? Array $stderr.puts "Skipping pkg upgrade test for %s" % name next end first, second = sources unless FileTest.exists?(first) and FileTest.exists?(second) $stderr.puts "Could not find upgrade test pkgs; skipping" return end pkg = nil assert_nothing_raised { pkg = Puppet.type(:package).create( :name => name, :ensure => :latest, :source => first ) } assert(pkg, "Failed to create package %s" % name) modpkg(pkg) assert(pkg.provider.latest, "Could not retrieve latest value") assert_events([:package_installed], pkg) assert_nothing_raised { pkg.retrieve } assert(pkg.insync?, "Package is not in sync") pkg.clear assert_nothing_raised { pkg[:source] = second } assert_events([:package_changed], pkg) assert_nothing_raised { pkg.retrieve } assert(pkg.insync?, "Package is not in sync") assert_nothing_raised { pkg[:ensure] = :absent } assert_events([:package_removed], pkg) assert_nothing_raised { pkg.retrieve } assert(pkg.insync?, "Package is not in sync") end end # Stupid darwin, not supporting package uninstallation if Facter["operatingsystem"].value == "Darwin" and FileTest.exists? "/Users/luke/Documents/Puppet/pkgtesting.pkg" def test_darwinpkgs pkg = nil assert_nothing_raised { pkg = Puppet::Type.type(:package).create( :name => "pkgtesting", :source => "/Users/luke/Documents/Puppet/pkgtesting.pkg", :ensure => :present, :provider => :apple ) } assert_nothing_raised { pkg.retrieve } if pkg.insync? Puppet.notice "Test package is already installed; please remove it" next end # The file installed, and the receipt @@tmpfiles << "/tmp/file" @@tmpfiles << "/Library/Receipts/pkgtesting.pkg" assert_events([:package_installed], pkg, "package") assert_nothing_raised { pkg.retrieve } assert(pkg.insync?, "Package is not insync") assert(FileTest.exists?("/tmp/pkgtesting/file"), "File did not get created") end end # Yay, gems. They're special because any OS can test them. if Puppet::Type.type(:package).provider(:gem).suitable? def test_list_gems gems = nil assert_nothing_raised { gems = Puppet::Type.type(:package).provider(:gem).list } gems.each do |gem| assert_equal(:gem, gem[:provider], "Type was not set correctly") end end def test_install_gems gem = nil name = "wxrubylayouts" assert_nothing_raised { gem = Puppet::Type.newpackage( :name => name, :ensure => "0.0.2", :provider => :gem ) } assert_nothing_raised { gem.retrieve } if gem.is(:ensure) != :absent $stderr.puts "Cannot test gem installation; %s is already installed" % name return end assert_events([:package_installed], gem) assert_nothing_raised { gem.retrieve } assert_equal("0.0.2", gem.is(:ensure), "Incorrect version was installed") latest = nil assert_nothing_raised { latest = gem.provider.latest } assert(latest != gem[:ensure], "Did not correctly find latest value") gem[:ensure] = :latest assert_events([:package_changed], gem) gem.retrieve assert("0.0.2" != gem.is(:ensure), "Package was not updated.") gem[:ensure] = :absent assert_events([:package_removed], gem) end else $stderr.puts "Install gems for gem tests" def test_failure_when_no_gems obj = nil assert_raise(ArgumentError) do Puppet::Type.newpackage( :name => "yayness", :provider => "gem", :ensure => "installed" ) end end end end if Puppet.type(:package).provider(:rpm).suitable? and FileTest.exists?("/home/luke/rpm/RPMS/i386/puppet-server-0.16.1-1.i386.rpm") # We have a special test here, because we don't actually want to install the # package, just make sure it's getting the "latest" value. def test_rpmlatest pkg = nil assert_nothing_raised { pkg = Puppet::Type.type(:package).create( :provider => :rpm, :name => "puppet-server", :source => "/home/luke/rpm/RPMS/i386/puppet-server-0.16.1-1.i386.rpm" ) } assert_equal("0.16.1-1", pkg.provider.latest, "RPM did not provide correct value for latest") end end def test_packagedefaults should = case Facter["operatingsystem"].value when "Debian": :apt when "Darwin": :apple when "RedHat": :rpm when "Fedora": :yum when "FreeBSD": :ports when "OpenBSD": :openbsd when "Solaris": :sun end default = Puppet.type(:package).defaultprovider assert(default, "No default package provider for %s" % Facter["operatingsystem"].value) if should assert_equal(should, default.name, "Incorrect default package format") end end end end # $Id$