diff --git a/lib/puppet/provider/package/aptitude.rb b/lib/puppet/provider/package/aptitude.rb index 80574bd88..7b74acedc 100755 --- a/lib/puppet/provider/package/aptitude.rb +++ b/lib/puppet/provider/package/aptitude.rb @@ -1,15 +1,22 @@ Puppet::Type.type(:package).provide :aptitude, :parent => :apt do desc "Package management via ``aptitude``." commands :aptitude => "/usr/bin/aptitude" commands :aptcache => "/usr/bin/apt-cache" ENV['DEBIAN_FRONTEND'] = "noninteractive" def aptcmd(arg) # Apparently aptitude hasn't always supported a -q flag. - aptitude(arg.gsub(/-q/,"")) + output = aptitude(arg.gsub(/-q/,"")) + + # Yay, stupid aptitude doesn't throw an error when the package is missing. + if output =~ /0 newly installed/ + raise Puppet::Error.new( + "Could not find package %s" % self.name + ) + end end end # $Id$ diff --git a/lib/puppet/provider/package/dpkg.rb b/lib/puppet/provider/package/dpkg.rb index 84fba92e8..f747a5a0c 100755 --- a/lib/puppet/provider/package/dpkg.rb +++ b/lib/puppet/provider/package/dpkg.rb @@ -1,85 +1,94 @@ Puppet::Type.type(:package).provide :dpkg do desc "Package management via ``dpkg``. Because this only uses ``dpkg`` and not ``apt``, you must specify the source of any packages you want to manage." commands :dpkg => "/usr/bin/dpkg" commands :dpkgquery => "/usr/bin/dpkg-query" def self.list packages = [] # list out all of the packages - open("| #{command(:dpkgquery)} -W --showformat '${Status} ${Package} ${Version}\\n'") { |process| + cmd = "#{command(:dpkgquery)} -W --showformat '${Status} ${Package} ${Version}\\n'" + Puppet.debug "Executing '%s'" % cmd + execpipe(cmd) do |process| # our regex for matching dpkg output regex = %r{^(\S+ +\S+ +\S+) (\S+) (\S+)$} fields = [:status, :name, :ensure] hash = {} # now turn each returned line into a package object process.each { |line| if match = regex.match(line) hash.clear fields.zip(match.captures) { |field,value| hash[field] = value } hash[:provider] = self.name packages.push Puppet.type(:package).installedpkg(hash) else raise Puppet::DevError, "Failed to match dpkg-query line %s" % line end } - } + end return packages end + def install + unless file = @model[:source] + raise ArgumentError, "You cannot install dpkg packages without a source" + end + dpkg "-i", file + end + def query packages = [] fields = [:desired, :error, :status, :name, :ensure] hash = {} # list out our specific package open("| #{command(:dpkgquery)} -W --showformat '${Status} ${Package} ${Version}\\n' %s" % @model[:name]) { |process| # our regex for matching dpkg-query output regex = %r{^(\S+) (\S+) (\S+) (\S+) (\S+)$} lines = process.readlines.collect {|l| l.chomp } line = lines[0] if match = regex.match(line) fields.zip(match.captures) { |field,value| hash[field] = value } else hash = {:ensure => :absent, :status => 'missing', :name => @model[:name], :error => 'ok'} end } if hash[:error] != "ok" raise Puppet::PackageError.new( "Package %s, version %s is in error state: %s" % [hash[:name], hash[:ensure], hash[:error]] ) end # DPKG can discuss packages that are no longer installed, so allow that. if hash[:status] != "installed" hash[:ensure] = :absent end return hash end def uninstall dpkg "-r %s" % @model[:name] end end # $Id$ diff --git a/lib/puppet/provider/package/gem.rb b/lib/puppet/provider/package/gem.rb index b0ecaafe6..0fb55c67c 100755 --- a/lib/puppet/provider/package/gem.rb +++ b/lib/puppet/provider/package/gem.rb @@ -1,101 +1,101 @@ # Ruby gems support. Puppet::Type.type(:package).provide :gem do desc "Ruby Gem support. By default uses remote gems, but you can specify the path to a local gem via ``source``." commands :gem => "gem" def self.gemlist(hash) command = "#{command(:gem)} list " if hash[:local] command += "--local " else command += "--remote " end if name = hash[:justme] command += name end begin list = execute(command).split("\n\n").collect do |set| if gemhash = gemsplit(set) gemhash[:provider] = :gem gemhash else nil end end.reject { |p| p.nil? } rescue Puppet::ExecutionFailure => detail raise Puppet::Error, "Could not list gems: %s" % detail end if hash[:justme] return list.shift else return list end end def self.gemsplit(desc) case desc when /^\*\*\*/: return nil when /^(\S+)\s+\((.+)\)\n/ name = $1 version = $2.split(/,\s*/)[0] return { :name => name, :ensure => version } else Puppet.warning "Could not match %s" % desc nil end end def self.list(justme = false) - gemlist(:local => true).each do |hash| + gemlist(:local => true).collect do |hash| Puppet::Type.type(:package).installedpkg(hash) end end def install(useversion = true) command = "install " if (! @model.should(:ensure).is_a? Symbol) and useversion command += "-v %s " % @model.should(:ensure) end if source = @model[:source] command += source else command += @model[:name] end gem command end def latest # This always gets the latest version available. hash = self.class.gemlist(:justme => @model[:name]) return hash[:ensure] end def query self.class.gemlist(:justme => @model[:name], :local => true) end def uninstall gem "uninstall -x -a #{@model[:name]}" end def update self.install(false) end def versionable? true end end # $Id$ diff --git a/lib/puppet/provider/parsedfile.rb b/lib/puppet/provider/parsedfile.rb index 144267623..ddb18ff17 100755 --- a/lib/puppet/provider/parsedfile.rb +++ b/lib/puppet/provider/parsedfile.rb @@ -1,313 +1,314 @@ require 'puppet' +require 'puppet/filetype' require 'puppet/util/fileparsing' # This provider can be used as the parent class for a provider that # parses and generates files. Its content must be loaded via the # 'prefetch' method, and the file will be written when 'flush' is called # on the provider instance. At this point, the file is written once # for every provider instance. # # Once the provider prefetches the data, it's the model's job to copy # that data over to the @is variables. class Puppet::Provider::ParsedFile < Puppet::Provider extend Puppet::Util::FileParsing class << self attr_accessor :default_target, :target end attr_accessor :state_hash def self.clean(hash) newhash = hash.dup [:record_type, :on_disk].each do |p| if newhash.include?(p) newhash.delete(p) end end return newhash end def self.clear @target_objects.clear @records.clear end def self.filetype unless defined? @filetype @filetype = Puppet::FileType.filetype(:flat) end return @filetype end def self.filetype=(type) if type.is_a?(Class) @filetype = type elsif klass = Puppet::FileType.filetype(type) @filetype = klass else raise ArgumentError, "Invalid filetype %s" % type end end # Flush all of the targets for which there are modified records. The only # reason we pass a record here is so that we can add it to the stack if # necessary -- it's passed from the instance calling 'flush'. def self.flush(record) # Make sure this record is on the list to be flushed. unless record[:on_disk] record[:on_disk] = true @records << record # If we've just added the record, then make sure our # target will get flushed. modified(record[:target] || default_target) end return unless defined?(@modified) and ! @modified.empty? flushed = [] @modified.sort { |a,b| a.to_s <=> b.to_s }.uniq.each do |target| Puppet.debug "Flushing %s provider target %s" % [@model.name, target] flush_target(target) flushed << target end @modified.reject! { |t| flushed.include?(t) } end # Flush all of the records relating to a specific target. def self.flush_target(target) target_object(target).write(to_file(target_records(target).reject { |r| r[:ensure] == :absent })) end # Return the header placed at the top of each generated file, warning # users that modifying this file manually is probably a bad idea. def self.header %{# HEADER: This file was autogenerated at #{Time.now} # HEADER: by puppet. While it can still be managed manually, it # HEADER: is definitely not recommended.\n} end # Add another type var. def self.initvars @records = [] @target_objects = {} @target = nil # Default to flat files @filetype = Puppet::FileType.filetype(:flat) super end # Return a list of all of the records we can find. def self.list prefetch() @records.find_all { |r| r[:record_type] == self.name }.collect { |r| clean(r) } end def self.list_by_name list.collect { |r| r[:name] } end # Create attribute methods for each of the model's non-metaparam attributes. def self.model=(model) [model.validstates, model.parameters].flatten.each do |attr| attr = symbolize(attr) define_method(attr) do # If it's not a valid field for this record type (which can happen # when different platforms support different fields), then just # return the should value, so the model shuts up. if @state_hash[attr] or self.class.valid_attr?(self.class.name, attr) @state_hash[attr] || :absent else @model.should(attr) end end define_method(attr.to_s + "=") do |val| # Mark that this target was modified. modeltarget = @model[:target] || self.class.default_target # If they're the same, then just mark that one as modified if @state_hash[:target] and @state_hash[:target] == modeltarget self.class.modified(modeltarget) else # Always mark the modeltarget as modified, and if there's # and old state_hash target, mark it as modified and replace # it. self.class.modified(modeltarget) if @state_hash[:target] self.class.modified(@state_hash[:target]) end @state_hash[:target] = modeltarget end @state_hash[attr] = val end end @model = model end # Mark a target as modified so we know to flush it. This only gets # used within the attr= methods. def self.modified(target) @modified ||= [] @modified << target unless @modified.include?(target) end # Retrieve all of the data from disk. There are three ways to know # while files to retrieve: We might have a list of file objects already # set up, there might be instances of our associated model and they # will have a path parameter set, and we will have a default path # set. We need to turn those three locations into a list of files, # prefetch each one, and make sure they're associated with each appropriate # model instance. def self.prefetch # Reset the record list. @records = [] targets().each do |target| prefetch_target(target) end end # Prefetch an individual target. def self.prefetch_target(target) @records += retrieve(target).each do |r| r[:on_disk] = true r[:target] = target r[:ensure] = :present end # Set current state on any existing resource instances. target_records(target).find_all { |i| i.is_a?(Hash) }.each do |record| # Find any model instances whose names match our instances. if instance = self.model[record[:name]] next unless instance.provider.is_a?(self) instance.provider.state_hash = record elsif self.respond_to?(:match) if instance = self.match(record) record[:name] = instance[:name] instance.provider.state_hash = record end end end end # Is there an existing record with this name? def self.record?(name) @records.find { |r| r[:name] == name } end # Retrieve the text for the file. Returns nil in the unlikely # event that it doesn't exist. def self.retrieve(path) # XXX We need to be doing something special here in case of failure. text = target_object(path).read if text.nil? or text == "" # there is no file return [] else # Set the target, for logging. old = @target begin @target = path self.parse(text) ensure @target = old end end end # Initialize the object if necessary. def self.target_object(target) @target_objects[target] ||= @filetype.new(target) @target_objects[target] end # Find all of the records for a given target def self.target_records(target) @records.find_all { |r| r[:target] == target } end # Find a list of all of the targets that we should be reading. This is # used to figure out what targets we need to prefetch. def self.targets targets = [] # First get the default target unless self.default_target raise Puppet::DevError, "Parsed Providers must define a default target" end targets << self.default_target # Then get each of the file objects targets += @target_objects.keys # Lastly, check the file from any model instances self.model.each do |model| targets << model[:target] end targets.uniq.reject { |t| t.nil? } end def create @model.class.validstates.each do |state| if value = @model.should(state) @state_hash[state] = value end end self.class.modified(@state_hash[:target] || self.class.default_target) return (@model.class.name.to_s + "_created").intern end def destroy # We use the method here so it marks the target as modified. self.ensure = :absent return (@model.class.name.to_s + "_deleted").intern end def exists? if @state_hash[:ensure] == :absent or @state_hash[:ensure].nil? return false else return true end end # Write our data to disk. def flush # Make sure we've got a target and name set. # If the target isn't set, then this is our first modification, so # mark it for flushing. unless @state_hash[:target] @state_hash[:target] = @model[:target] || self.class.default_target self.class.modified(@state_hash[:target]) end @state_hash[:name] ||= @model.name self.class.flush(@state_hash) end def initialize(model) super # See if there's already a matching state_hash in the records list; # else, use a default value. # We provide a default for 'ensure' here, because the provider will # override it if the thing exists, but it won't touch it if it doesn't # exist. @state_hash = self.class.record?(model[:name]) || {:record_type => self.class.name, :ensure => :absent} end end # $Id$ diff --git a/test/data/providers/package/testpackages.yaml b/test/data/providers/package/testpackages.yaml new file mode 100644 index 000000000..47998ed33 --- /dev/null +++ b/test/data/providers/package/testpackages.yaml @@ -0,0 +1,64 @@ +--- +- :operatingsystem: Solaris + :files: + - /usr/local/pkg/rdesktop-1.3.1-sol10-intel-local + - /usr/local/pkg/rdesktop-1.4.1-sol10-x86-local + :hardwareisa: i386 + :name: SMCrdesk + :operatingsystemrelease: "5.10" + :provider: :sun +- :operatingsystem: Solaris + :name: cabextract + :provider: :blastwave +- :operatingsystem: Solaris + :files: + - /usr/local/pkg/arc-5.21e-sol8-sparc-local + :hardwareisa: sparc + :name: SMCarc + :operatingsystemrelease: "5.8" + :provider: :sun +- :operatingsystem: Solaris + :files: + - /usr/local/pkg/arc-5.21e-sol8-intel-local + :hardwareisa: i386 + :name: SMCarc + :operatingsystemrelease: "5.8" + :provider: :sun +- :operatingsystem: OpenBSD + :files: + - ftp://ftp.usa.openbsd.org/pub/OpenBSD/3.8/packages/i386/aalib-1.2-no_x11.tgz + :name: aalib + :provider: :openbsd +- :operatingsystem: Debian + :name: zec + :provider: :apt +- :operatingsystem: Debian + :name: zec + :provider: :aptitude +- :operatingsystem: Fedora + :name: wv + :provider: :yum +- :operatingsystem: + - Fedora + - CentOS + - RedHat + :files: + - /home/luke/rpm/RPMS/noarch/enhost-1.0.1-1.noarch.rpm + - /home/luke/rpm/RPMS/noarch/enhost-1.0.2-1.noarch.rpm + :name: enhost + :provider: :rpm +- :operatingsystem: Darwin + :name: aop + :provider: :darwinports +- :operatingsystem: FreeBSD + :name: yahtzee + :provider: :ports +- :provider: :apple + :files: + - /Users/luke/Documents/Puppet/pkgtesting.pkg + :name: pkgtesting +- :provider: :gem + :name: wxrubylayouts + :versions: + - 0.0.2 + - 0.0.3 diff --git a/test/data/snippets/aliastest.pp b/test/data/snippets/aliastest.pp index 2a8fc9cb9..f2b61592e 100644 --- a/test/data/snippets/aliastest.pp +++ b/test/data/snippets/aliastest.pp @@ -1,16 +1,16 @@ file { "a file": path => "/tmp/aliastest", ensure => file } file { "another": path => "/tmp/aliastest2", ensure => file, - require => file["a file"] + require => File["a file"] } file { "a third": path => "/tmp/aliastest3", ensure => file, - require => file["/tmp/aliastest"] + require => File["/tmp/aliastest"] } diff --git a/test/lib/puppettest/fakes.rb b/test/lib/puppettest/fakes.rb index 338afe575..137042d7f 100644 --- a/test/lib/puppettest/fakes.rb +++ b/test/lib/puppettest/fakes.rb @@ -1,190 +1,190 @@ require 'puppettest' module PuppetTest # A baseclass for the faketypes. class FakeModel include Puppet::Util class << self attr_accessor :name, :realmodel @name = :fakemodel end def self.namevar @realmodel.namevar end def self.validstates Puppet::Type.type(@name).validstates end def self.validstate?(name) Puppet::Type.type(@name).validstate?(name) end def self.to_s "Fake%s" % @name.to_s.capitalize end def [](param) if @realmodel.attrtype(param) == :state @is[param] else @params[param] end end def []=(param, value) param = symbolize(param) unless @realmodel.validattr?(param) raise Puppet::DevError, "Invalid attribute %s for %s" % [param, @realmodel.name] end if @realmodel.attrtype(param) == :state @should[param] = value else @params[param] = value end end def initialize(name) @realmodel = Puppet::Type.type(self.class.name) raise "Could not find type #{self.class.name}" unless @realmodel @is = {} @should = {} @params = {} self[@realmodel.namevar] = name end def inspect "%s(%s)" % [self.class.to_s.sub(/.+::/, ''), super()] end def is(param) @is[param] end def should(param) @should[param] end def to_hash hash = @params.dup [@is, @should].each do |h| h.each do |p, v| hash[p] = v end end hash end def name self[:name] end end class FakeProvider attr_accessor :model class << self attr_accessor :name, :model, :methods end # A very low number, so these never show up as defaults via the standard # algorithms. def self.defaultnum -50 end # Set up methods to fake things def self.apimethods(*ary) @model.validstates.each do |state| ary << state unless ary.include? state end attr_accessor(*ary) @methods = ary end def self.default? false end def self.initvars @calls = Hash.new do |hash, key| hash[key] = 0 end end def self.suitable? true end def clear @model = nil end def initialize(model) @model = model end end class FakeParsedProvider < FakeProvider def hash ret = {} instance_variables.each do |v| v = v.sub("@", '') if val = self.send(v) ret[v.intern] = val end end return ret end - + def store(hash) hash.each do |n, v| method = n.to_s + "=" if respond_to? method send(method, v) end end end end @@fakemodels = {} @@fakeproviders = {} def fakemodel(type, name, options = {}) type = type.intern if type.is_a? String unless @@fakemodels.include? type @@fakemodels[type] = Class.new(FakeModel) @@fakemodels[type].name = type model = Puppet::Type.type(type) raise("Could not find type %s" % type) unless model @@fakemodels[type].realmodel = model end obj = @@fakemodels[type].new(name) options.each do |name, val| obj[name] = val end obj end module_function :fakemodel def fakeprovider(type, model) type = type.intern if type.is_a? String unless @@fakeproviders.include? type @@fakeproviders[type] = Class.new(FakeModel) do @name = type end end @@fakeproviders[type].new(model) end module_function :fakeprovider end # $Id$ diff --git a/test/providers/package.rb b/test/providers/package.rb index 1b74e8aac..6b175c44a 100755 --- a/test/providers/package.rb +++ b/test/providers/package.rb @@ -1,83 +1,234 @@ #!/usr/bin/env ruby $:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ require 'etc' require 'puppet/type' require 'puppettest' class TestPackageProvider < Test::Unit::TestCase include PuppetTest - def setup - super - @provider = nil - assert_nothing_raised { - @provider = Puppet::Type.type(:package).defaultprovider + + # Load the testpackages hash. + def self.load_test_packages + require 'yaml' + file = File.join(PuppetTest.datadir(), "providers", "package", "testpackages.yaml") + unless FileTest.exists?(file) + raise "Could not find file %s" % file + end + array = YAML::load(File.read(file)).collect { |hash| + # Stupid ruby 1.8.1. YAML is sometimes broken such that + # symbols end up being strings with the : in them. + hash.each do |name, value| + if name.is_a?(String) and name =~ /^:/ + hash.delete(name) + name = name.sub(/^:/, '').intern + hash[name] = value + end + if value.is_a?(String) and value =~ /^:/ + hash[name] = value.sub(/^:/, '').intern + end + end } - assert(@provider, "Could not find default package provider") - assert(@provider.name != :fake, "Got a fake provider") + return array end - def test_nothing + def self.suitable_test_packages + list = load_test_packages + providers = {} + Puppet::Type.type(:package).suitableprovider.each do |provider| + providers[provider.name] = provider + end + facts = {} + Facter.to_hash.each do |fact, value| + facts[fact.downcase.intern] = value.downcase.intern + end + list.find_all { |hash| # First find the matching providers + hash.include?(:provider) and providers.include?(hash[:provider]) + }.reject { |hash| # Then find matching fact sets + facts.detect do |fact, value| + # We're detecting unmatched facts, but we also want to + # delete the facts so they don't show up later. + if fval = hash[fact] + hash.delete(fact) + fval = [fval] unless fval.is_a?(Array) + fval = fval.collect { |v| v.downcase.intern } + ! fval.include?(value) + end + end + } end - if Facter["operatingsystem"].value == "Solaris" and Puppet::SUIDManager.uid == 0 - if Puppet.type(:package).provider(:blastwave).suitable? - # FIXME The packaging crap needs to be rewritten to support testing - # multiple package types on the same platform. - def test_list_blastwave - pkgs = nil - assert_nothing_raised { - pkgs = Puppet::Type.type(:package).provider(:blastwave).list - } + def assert_absent(provider, msg = "package not absent") + result = nil + assert_nothing_raised("Could not query provider") do + result = provider.query + end + if result.nil? + assert_nil(result) + elsif result.is_a?(Hash) + assert_equal(:absent, result[:ensure], msg) + else + raise "dunno how to handle %s" % result.inspect + end + end - pkgs.each do |pkg| - if pkg[:name] =~ /^CSW/ - assert_equal(:blastwave, pkg[:provider], - "Type was not set correctly") - end + def assert_not_absent(provider, msg = "package not installed") + result = nil + assert_nothing_raised("Could not query provider") do + result = provider.query end + assert_instance_of(Hash, result, "query did not return hash") + assert(result[:ensure] != :absent, msg) end - def test_install_blastwave + # Run a package through all of its paces. FIXME This should use the + # provider, not the package, duh. + def run_package_installation_test(hash) + # Turn the hash into a package + if files = hash[:files] + hash.delete(:files) + if files.is_a?(Array) + hash[:source] = files.shift + else + hash[:source] = files + files = [] + end + else + files = [] + end + + if versions = hash[:versions] + hash.delete(:versions) + else + versions = [] + end + + # Start out by just making sure it's installed + if versions.empty? + hash[:ensure] = :present + else + hash[:ensure] = versions.shift + end + + if hash[:source] + unless FileTest.exists?(hash[:source]) + $stderr.puts "Create a package at %s for testing" % hash[:source] + return + end + end + pkg = nil - name = "cabextract" - model = fakemodel(:package, name) - assert_nothing_raised { - pkg = Puppet::Type.type(:package).provider(:blastwave).new(model) - } + assert_nothing_raised( + "Could not turn %s into a package" % hash.inspect + ) do + pkg = Puppet::Type.newpackage(hash) + end + + # Make any necessary modifications. + modpkg(pkg) + + provider = pkg.provider + + assert(provider, "Could not retrieve provider") + + assert_absent(provider) - if hash = pkg.query and hash[:ensure] != :absent - p hash - $stderr.puts "Cannot test pkg installation; %s is already installed" % - name + if Process.uid != 0 + $stderr.puts "Run as root for full package tests" return end - assert_nothing_raised { - pkg.install - } + cleanup do + pkg[:ensure] = :absent + assert_apply(pkg) + end - hash = nil - assert(hash = pkg.query, - "package did not install") - assert(hash[:ensure] != :absent, - "package did not install") + assert_nothing_raised("Could not install package") do + provider.install + end - latest = nil - assert_nothing_raised { - latest = pkg.latest - } - assert(latest, "Could not find latest package version") - assert_nothing_raised { - pkg.uninstall - } + assert_not_absent(provider, "package did not install") + + # If there are any remaining files, then test upgrading from there + unless files.empty? + pkg[:source] = files.shift + current = provider.query + assert_nothing_raised("Could not upgrade") do + provider.update + end + new = provider.query + assert(current != new, "package was not upgraded: %s did not change" % + current.inspect) + end + + unless versions.empty? + pkg[:ensure] = versions.shift + current = provider.query + assert_nothing_raised("Could not upgrade") do + provider.update + end + new = provider.query + assert(current != new, "package was not upgraded: %s did not change" % + current.inspect) + end + + # Now remove the package + assert_nothing_raised do + provider.uninstall + end + + assert_absent(provider) end - else - $stderr.puts "No pkg-get scripting; skipping blastwave tests" + + # Now create a separate test method for each package + suitable_test_packages.each do |hash| + mname = ["test", hash[:name].to_s, hash[:provider].to_s].join("_").intern + + if method_defined?(mname) + warn "Already a test method defined for %s" % mname + else + define_method(mname) do + run_package_installation_test(hash) + end + end + end + + def modpkg(pkg) + case pkg[:provider] + when :sun: + pkg[:adminfile] = "/usr/local/pkg/admin_file" + end end + + # Make sure providers throw an error when you tell them to install a + # non-existent package. + def test_no_such_package + Puppet::Type.type(:package).suitableprovider.each do |provider| + assert_raise(ArgumentError, Puppet::Error, Puppet::ExecutionFailure, + "Successfully installed nonexistent package with %s" % provider.name) { + pkg = Puppet::Type.newpackage :name => "nosuch%s" % provider.name.to_s, + :provider => provider.name + provider = pkg.provider + provider.install + } + end + end + + # Make sure all of the suitable providers on our platform can successfully + # list. + def test_listing + Puppet::Type.type(:package).suitableprovider.each do |provider| + result = nil + assert_nothing_raised("Could not list %s packages" % provider.name) do + result = provider.list + end + result.each do |pkg| + assert_instance_of(Puppet::Type.type(:package), pkg) + end + end end end # $Id$ diff --git a/test/types/package.rb b/test/types/package.rb index 8a542d62c..009bedf33 100755 --- a/test/types/package.rb +++ b/test/types/package.rb @@ -1,498 +1,86 @@ #!/usr/bin/env ruby $:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ 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 + @type = Puppet::Type.type(:package) 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" + # This is a bare-minimum test and *really* needs to do much more. + def test_package_actions + @type.provide :fake, :parent => PuppetTest::FakeProvider do + apimethods :ensure + def install + self.ensure = @model.should(:ensure) 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"} + def uninstall + self.ensure = :absent 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(obj.is(:ensure) != :absent, "Package %s is not installed" % obj.title) - - 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 + def query + case self.ensure + when :absent, nil: nil + else + {:ensure => self.ensure} + end 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, Puppet::ExecutionFailure, - "Successfully installed nonexistent package") { - state.sync - } - end - - 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_events([], comp, "package") - - 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" - return - end - - # The file installed, and the receipt - cleanup do - FileUtils.rmtree("/tmp/file") - FileUtils.rmtree("/Library/Receipts/pkgtesting.pkg") - end - - 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") + pkg = nil + assert_nothing_raised do + pkg = @type.create :name => "testing", :provider => :fake 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(pkg, "did not create package") - assert_nothing_raised { - gem.retrieve - } - - if gem.is(:ensure) != :absent - $stderr.puts "Cannot test gem installation; %s is already installed" % - name - return + assert_nothing_raised do + pkg.retrieve 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_equal(:absent, pkg.is(:ensure), "package not considered missing") + assert_equal(:present, pkg.should(:ensure), + "package did not default to installed") - assert("0.0.2" != gem.is(:ensure), - "Package was not updated.") + assert_events([:package_installed], pkg) - 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 + pkg[:ensure] = :absent + assert_events([:package_removed], pkg) end def test_packagedefaults should = case Facter["operatingsystem"].value when "Debian": :apt when "Darwin": :apple - when "RedHat": :rpm + when "RedHat": :up2date 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) + unless default = Puppet.type(:package).defaultprovider + $stderr.puts "no default provider for %s" % + Facter["operatingsystem"].value + return + end if should assert_equal(should, default.name, "Incorrect default package format") end end end -end # $Id$ diff --git a/test/types/sshkey.rb b/test/types/sshkey.rb index d4e8c59a5..533c97dad 100755 --- a/test/types/sshkey.rb +++ b/test/types/sshkey.rb @@ -1,170 +1,170 @@ #!/usr/bin/env ruby $:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/ require 'puppettest' require 'puppet' require 'puppet/type/sshkey' require 'facter' class TestSSHKey < Test::Unit::TestCase include PuppetTest def setup super # god i'm lazy @sshkeytype = Puppet.type(:sshkey) @provider = @sshkeytype.defaultprovider # Make sure they aren't using something funky like netinfo unless @provider.name == :parsed @sshkeytype.defaultprovider = @sshkeytype.provider(:parsed) end cleanup do @sshkeytype.defaultprovider = nil end if @provider.respond_to?(:default_target) oldpath = @provider.default_target cleanup do @provider.default_target = oldpath end @provider.default_target = tempfile() end end def teardown super if @provider.respond_to?(:clear) @provider.clear end end def mkkey key = nil if defined? @kcount @kcount += 1 else @kcount = 1 end assert_nothing_raised { key = @sshkeytype.create( :name => "host%s.madstop.com" % @kcount, :key => "%sAAAAB3NzaC1kc3MAAACBAMnhSiku76y3EGkNCDsUlvpO8tRgS9wL4Eh54WZfQ2lkxqfd2uT/RTT9igJYDtm/+UHuBRdNGpJYW1Nw2i2JUQgQEEuitx4QKALJrBotejGOAWxxVk6xsh9xA0OW8Q3ZfuX2DDitfeC8ZTCl4xodUMD8feLtP+zEf8hxaNamLlt/AAAAFQDYJyf3vMCWRLjTWnlxLtOyj/bFpwAAAIEAmRxxXb4jjbbui9GYlZAHK00689DZuX0EabHNTl2yGO5KKxGC6Esm7AtjBd+onfu4Rduxut3jdI8GyQCIW8WypwpJofCIyDbTUY4ql0AQUr3JpyVytpnMijlEyr41FfIb4tnDqnRWEsh2H7N7peW+8DWZHDFnYopYZJ9Yu4/jHRYAAACAERG50e6aRRb43biDr7Ab9NUCgM9bC0SQscI/xdlFjac0B/kSWJYTGVARWBDWug705hTnlitY9cLC5Ey/t/OYOjylTavTEfd/bh/8FkAYO+pWdW3hx6p97TBffK0b6nrc6OORT2uKySbbKOn0681nNQh4a6ueR3JRppNkRPnTk5c=" % @kcount, :type => "ssh-dss", :alias => ["192.168.0.%s" % @kcount] ) } return key end def test_list assert_nothing_raised { Puppet.type(:sshkey).defaultprovider.list } count = 0 @sshkeytype.each do |h| count += 1 end assert_equal(0, count, "Found sshkeys in empty file somehow") end def test_simplekey key = mkkey assert_apply(key) assert(key.provider.exists?, "Key did not get created") end def test_moddingkey key = mkkey() assert_events([:sshkey_created], key) key.retrieve key[:alias] = %w{madstop kirby yayness} assert_events([:sshkey_changed], key) end def test_aliasisstate assert_equal(:state, @sshkeytype.attrtype(:alias)) end def test_multivalues key = mkkey assert_raise(Puppet::Error) { key[:alias] = "puppetmasterd yayness" } end def test_puppetalias key = mkkey() assert_nothing_raised { key[:alias] = "testing" } same = key.class["testing"] assert(same, "Could not retrieve by alias") end def test_removal sshkey = mkkey() assert_nothing_raised { sshkey[:ensure] = :present } assert_events([:sshkey_created], sshkey) assert(sshkey.provider.exists?, "key was not created") assert_nothing_raised { sshkey[:ensure] = :absent } - assert_events([:sshkey_deleted], sshkey) + assert_events([:sshkey_removed], sshkey) assert(! sshkey.provider.exists?, "Key was not deleted") assert_events([], sshkey) end # Make sure changes actually modify the file. def test_modifyingfile keys = [] names = [] 3.times { k = mkkey() #h[:ensure] = :present #h.retrieve keys << k names << k.name } assert_apply(*keys) keys.clear Puppet.type(:sshkey).clear newkey = mkkey() #newkey[:ensure] = :present names << newkey.name assert_apply(newkey) # Verify we can retrieve that info assert_nothing_raised("Could not retrieve after second write") { newkey.provider.class.prefetch newkey.retrieve } # And verify that we have data for everything names.each { |name| key = Puppet.type(:sshkey)[name] || Puppet.type(:sshkey).create(:name => name) assert(key, "Could not retrieve key for %s" % name) assert(key.provider.exists?, "key %s is missing" % name) } end end # $Id$