diff options
-rw-r--r-- | lib/puppet/parameter.rb | 59 | ||||
-rw-r--r-- | lib/puppet/property.rb | 60 | ||||
-rw-r--r-- | lib/puppet/provider.rb | 5 | ||||
-rw-r--r-- | lib/puppet/provider/package.rb | 1 | ||||
-rw-r--r-- | lib/puppet/type/package.rb | 69 | ||||
-rw-r--r-- | lib/puppet/util/provider_features.rb | 22 | ||||
-rwxr-xr-x | spec/integration/ral/types/package.rb | 24 | ||||
-rwxr-xr-x | spec/unit/ral/types/package.rb | 246 | ||||
-rwxr-xr-x | test/ral/providers/package.rb | 2 | ||||
-rwxr-xr-x | test/ral/types/package.rb | 158 |
10 files changed, 349 insertions, 297 deletions
diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index fae0587e1..31e009af5 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -289,9 +289,6 @@ class Puppet::Parameter raise Puppet::DevError, "No resource set for %s" % self.class.name end - # LAK 2007-05-09: Keep the @parent around for backward compatibility. - #@parent = @resource - if ! self.metaparam? and klass = Puppet::Type.metaparamclass(self.class.name) setup_shadow(klass) end @@ -378,36 +375,37 @@ class Puppet::Parameter vals = self.class.values regs = self.class.regexes - if regs.is_a? Hash # this is true on properties - regs = regs.keys - end - if vals.empty? and regs.empty? - # This parameter isn't using defined values to do its work. - return - end + # this is true on properties + regs = regs.keys if regs.is_a?(Hash) + + # This parameter isn't using defined values to do its work. + return if vals.empty? and regs.empty? + newval = value - unless value.is_a?(Symbol) - newval = value.to_s.intern - end + newval = value.to_s.intern unless value.is_a?(Symbol) - unless vals.include?(newval) or - self.class.alias(newval) or - self.class.match?(value) # We match the string, not the symbol - str = "Invalid '%s' value %s. " % - [self.class.name, value.inspect] + name = newval - unless vals.empty? - str += "Valid values are %s. " % vals.join(", ") - end + unless vals.include?(newval) or name = self.class.alias(newval) or name = self.class.match?(value) # We match the string, not the symbol + str = "Invalid '%s' value %s. " % + [self.class.name, value.inspect] - unless regs.empty? - str += "Valid values match %s." % regs.collect { |r| - r.to_s - }.join(", ") - end + unless vals.empty? + str += "Valid values are %s. " % vals.join(", ") + end + + unless regs.empty? + str += "Valid values match %s." % regs.collect { |r| + r.to_s + }.join(", ") + end - raise ArgumentError, str + raise ArgumentError, str end + + # Now check for features. + name = name[0] if name.is_a?(Array) # This is true for regexes. + validate_features_per_value(name) if is_a?(Puppet::Property) end def remove @@ -484,5 +482,12 @@ class Puppet::Parameter def to_s s = "Parameter(%s)" % self.name end + + # Make sure that we've got all of the required features for a given value. + def validate_features_per_value(value) + if features = self.class.value_option(value, :required_features) + raise ArgumentError, "Provider must have features '%s' to set '%s' to '%s'" % [features, self.class.name, value] unless provider.satisfies?(features) + end + end end diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb index 84620bfb6..f3d879ee2 100644 --- a/lib/puppet/property.rb +++ b/lib/puppet/property.rb @@ -67,9 +67,7 @@ class Property < Puppet::Parameter # Retrieve an option set when a value was defined. def self.value_option(name, option) - if option.is_a?(String) - option = symbolize(option) - end + option = option.to_sym if hash = @parameteroptions[name] hash[option] else @@ -209,28 +207,28 @@ class Property < Puppet::Parameter # Figure out which event to return. def event(name, event = nil) if value_event = self.class.value_option(name, :event) - return value_event - else - if event and event.is_a?(Symbol) - if event == :nochange - return nil - else - return event - end + return value_event + end + + if event and event.is_a?(Symbol) + if event == :nochange + return nil else - if self.class.name == :ensure - event = case self.should - when :present: (@resource.class.name.to_s + "_created").intern - when :absent: (@resource.class.name.to_s + "_removed").intern - else - (@resource.class.name.to_s + "_changed").intern - end - else - event = (@resource.class.name.to_s + "_changed").intern - end + return event end end + if self.class.name == :ensure + event = case self.should + when :present: (@resource.class.name.to_s + "_created").intern + when :absent: (@resource.class.name.to_s + "_removed").intern + else + (@resource.class.name.to_s + "_changed").intern + end + else + event = (@resource.class.name.to_s + "_changed").intern + end + return event end @@ -297,8 +295,7 @@ class Property < Puppet::Parameter # Send a log message. def log(msg) unless @resource[:loglevel] - self.devfail "Parent %s has no loglevel" % - @resource.name + self.devfail "Parent %s has no loglevel" % @resource.name end Puppet::Util::Log.create( :level => @resource[:loglevel], @@ -338,10 +335,7 @@ class Property < Puppet::Parameter # provider. In other words, if the property name is 'gid', we'll call # 'provider.gid' to retrieve the current value. def retrieve - is = provider.send(self.class.name) -# puts "IS is: " + is.to_s -# puts "and its an array!!!" if is.is_a? Array - return is + provider.send(self.class.name) end # Set our value, using the provider, an associated block, or both. @@ -420,17 +414,9 @@ class Property < Puppet::Parameter end end - # The default 'sync' method only selects among a list of registered - # values. + # The default 'sync' method only selects among a list of registered # values. def sync -# if self.insync? -# self.info "already in sync" -# return nil -# end - unless self.class.values - self.devfail "No values defined for %s" % - self.class.name - end + self.devfail("No values defined for %s" % self.class.name) unless self.class.values if value = self.should set(value) diff --git a/lib/puppet/provider.rb b/lib/puppet/provider.rb index 26c1254e3..e73bb0cb6 100644 --- a/lib/puppet/provider.rb +++ b/lib/puppet/provider.rb @@ -64,6 +64,11 @@ class Puppet::Provider end end + # Is the provided feature a declared feature? + def self.declared_feature?(name) + defined?(@declared_features) and @declared_features.include?(name) + end + # Does this implementation match all of the default requirements? If # defaults are empty, we return false. def self.default? diff --git a/lib/puppet/provider/package.rb b/lib/puppet/provider/package.rb index 094bfb9f4..f7ff7e55a 100644 --- a/lib/puppet/provider/package.rb +++ b/lib/puppet/provider/package.rb @@ -27,4 +27,3 @@ class Puppet::Provider::Package < Puppet::Provider @property_hash.dup end end - diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index e04f651a9..616362206 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -4,7 +4,6 @@ # systems. module Puppet - class PackageError < Puppet::Error; end newtype(:package) do @doc = "Manage packages. There is a basic dichotomy in package support right now: Some package types (e.g., yum and apt) can @@ -108,25 +107,15 @@ module Puppet @lateststamp ||= (Time.now.to_i - 1000) # Iterate across all of the should values, and see how they # turn out. + @should.each { |should| case should when :present - unless [:absent, :purged].include?(is) - return true - end + return true unless [:absent, :purged].include?(is) when :latest # Short-circuit packages that are not present - if is == :absent - return false - end - - unless provider.respond_to?(:latest) - self.fail( - "Package type %s does not support specifying 'latest'" % - @resource[:provider] - ) - end - + return false if is == :absent or is == :purged + # Don't run 'latest' more than about every 5 minutes if @latest and ((Time.now.to_i - @lateststamp) / 60) < 5 #self.debug "Skipping latest check" @@ -153,9 +142,9 @@ module Puppet [is.inspect, @resource.name, @latest.inspect] end when :absent - if is == :absent or is == :purged - return true - end + return true if is == :absent or is == :purged + when :purged + return true if is == :purged when is return true end @@ -312,46 +301,6 @@ module Puppet autos end - @listed = false - - @allowedmethods = [:types] - - class << self - attr_reader :listed - end - - def self.clear - @listed = false - super - end - - # Create a new package object from listed information - def self.installedpkg(hash) - unless hash.include? :provider - raise Puppet::DevError, "Got installed package with no provider" - end - # this is from code, so we don't have to do as much checking - name = hash[:name] - hash.delete(:name) - - object = self[name] || self.create(:name => name) - object.setparams(hash) - - return object - end - - # Iterate across all packages of a given type and mark them absent - # if they are not in the list - def self.markabsent(pkgtype, packages) - # Mark any packages we didn't find as absent - self.each do |pkg| - next unless packages[:provider] == pkgtype - unless packages.include? pkg - pkg.provider.send(:ensure, :absent) - end - end - end - # This only exists for testing. def clear if obj = @parameters[:ensure] @@ -365,10 +314,6 @@ module Puppet @provider.get(:ensure) != :absent end - # okay, there are two ways that a package could be created... - # either through the language, in which case the hash's values should - # be set in 'should', or through comparing against the system, in which - # case the hash's values should be set in 'is' def initialize(params) self.initvars provider = nil diff --git a/lib/puppet/util/provider_features.rb b/lib/puppet/util/provider_features.rb index d5f272420..3a9d4262d 100644 --- a/lib/puppet/util/provider_features.rb +++ b/lib/puppet/util/provider_features.rb @@ -55,9 +55,7 @@ module Puppet::Util::ProviderFeatures # required to determine if the feature is present. def feature(name, docs, hash = {}) @features ||= {} - if @features.include?(name) - raise Puppet::DevError, "Feature %s is already defined" % name - end + raise(Puppet::DevError, "Feature %s is already defined" % name) if @features.include?(name) begin obj = ProviderFeature.new(name, docs, hash) @features[obj.name] = obj @@ -133,7 +131,8 @@ module Puppet::Util::ProviderFeatures } end - # Create a method that will list all functional features. + # Create a method that will determine if a provided list of + # features are satisfied by the curred provider. @feature_module.send(:define_method, :satisfies?) do |*needed| ret = true needed.flatten.each do |feature| @@ -150,13 +149,7 @@ module Puppet::Util::ProviderFeatures @features.each do |name, feature| method = name.to_s + "?" @feature_module.send(:define_method, method) do - if defined? @declared_features and @declared_features.include?(name) - true - elsif feature.available?(self) - true - else - false - end + self.class.declared_feature?(name) or feature.available?(self) end end @@ -173,5 +166,12 @@ module Puppet::Util::ProviderFeatures end @feature_module end + + # Return the actual provider feature instance. Really only used for testing. + def provider_feature(name) + return nil unless defined?(@features) + + @features[name] + end end diff --git a/spec/integration/ral/types/package.rb b/spec/integration/ral/types/package.rb new file mode 100755 index 000000000..20567629d --- /dev/null +++ b/spec/integration/ral/types/package.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/package' + +describe Puppet::Type::Package, "when choosing a default package provider" do + before do + # the default provider is cached. + Puppet::Type::Package.defaultprovider = nil + end + + def provider_name(os) + {"Debian" => :apt, "Darwin" => :apple, "RedHat" => :up2date, "Fedora" => :yum, "FreeBSD" => :ports, "OpenBSD" => :openbsd, "Solaris" => :sun}[os] + end + + it "should have a default provider" do + Puppet::Type::Package.defaultprovider.should_not be_nil + end + + it "should choose the correct provider each platform" do + Puppet::Type::Package.defaultprovider.name.should == provider_name(Facter.value(:operatingsystem)) + end +end diff --git a/spec/unit/ral/types/package.rb b/spec/unit/ral/types/package.rb new file mode 100755 index 000000000..f037ef5c2 --- /dev/null +++ b/spec/unit/ral/types/package.rb @@ -0,0 +1,246 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +require 'puppet/type/package' + +$platform = Facter["operatingsystem"].value + +describe Puppet::Type::Package do + it "should have an :installable feature that requires the :install method" do + Puppet::Type::Package.provider_feature(:installable).methods.should == [:install] + end + + it "should have an :uninstallable feature that requires the :uninstall method" do + Puppet::Type::Package.provider_feature(:uninstallable).methods.should == [:uninstall] + end + + it "should have an :upgradeable feature that requires :update and :latest methods" do + Puppet::Type::Package.provider_feature(:upgradeable).methods.should == [:update, :latest] + end + + it "should have a :purgeable feature that requires the :purge latest method" do + Puppet::Type::Package.provider_feature(:purgeable).methods.should == [:purge] + end + + it "should have a :versionable feature" do + Puppet::Type::Package.provider_feature(:purgeable).should_not be_nil + end + + it "should default to being installed" do + pkg = Puppet::Type::Package.create(:name => "yay") + pkg.should(:ensure).should == :present + end + + after { Puppet::Type::Package.clear } +end + +describe Puppet::Type::Package, "when validating attributes" do + [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom].each do |param| + it "should have a #{param} parameter" do + Puppet::Type::Package.attrtype(param).should == :param + end + end + + it "should have an ensure property" do + Puppet::Type::Package.attrtype(:ensure).should == :property + end +end + +describe Puppet::Type::Package, "when validating attribute values" do + before do + @provider = stub 'provider', :class => Puppet::Type::Package.defaultprovider, :clear => nil + Puppet::Type::Package.defaultprovider.expects(:new).returns(@provider) + end + + it "should support :present as a value to :ensure" do + Puppet::Type::Package.create(:name => "yay", :ensure => :present) + end + + it "should alias :installed to :present as a value to :ensure" do + pkg = Puppet::Type::Package.create(:name => "yay", :ensure => :installed) + pkg.should(:ensure).should == :present + end + + it "should support :absent as a value to :ensure" do + Puppet::Type::Package.create(:name => "yay", :ensure => :absent) + end + + it "should support :purged as a value to :ensure if the provider has the :purgeable feature" do + @provider.expects(:satisfies?).with(:purgeable).returns(true) + Puppet::Type::Package.create(:name => "yay", :ensure => :purged) + end + + it "should not support :purged as a value to :ensure if the provider does not have the :purgeable feature" do + @provider.expects(:satisfies?).with(:purgeable).returns(false) + proc { Puppet::Type::Package.create(:name => "yay", :ensure => :purged) }.should raise_error(Puppet::Error) + end + + it "should support :latest as a value to :ensure if the provider has the :upgradeable feature" do + @provider.expects(:satisfies?).with(:upgradeable).returns(true) + Puppet::Type::Package.create(:name => "yay", :ensure => :latest) + end + + it "should not support :latest as a value to :ensure if the provider does not have the :upgradeable feature" do + @provider.expects(:satisfies?).with(:upgradeable).returns(false) + proc { Puppet::Type::Package.create(:name => "yay", :ensure => :latest) }.should raise_error(Puppet::Error) + end + + it "should support version numbers as a value to :ensure if the provider has the :versionable feature" do + @provider.expects(:satisfies?).with(:versionable).returns(true) + Puppet::Type::Package.create(:name => "yay", :ensure => "1.0") + end + + it "should not support version numbers as a value to :ensure if the provider does not have the :versionable feature" do + @provider.expects(:satisfies?).with(:versionable).returns(false) + proc { Puppet::Type::Package.create(:name => "yay", :ensure => "1.0") }.should raise_error(Puppet::Error) + end + + it "should only accept files and URLs as values to :source" do + proc { Puppet::Type::Package.create(:name => "yay", :source => "stuff") }.should raise_error(Puppet::Error) + end + + after { Puppet::Type::Package.clear } +end + +module PackageEvaluationTesting + def setup + @provider = stub 'provider', :class => Puppet::Type::Package.defaultprovider, :clear => nil, :satisfies? => true, :name => :mock + Puppet::Type::Package.defaultprovider.stubs(:new).returns(@provider) + @package = Puppet::Type::Package.create(:name => "yay") + + @configuration = Puppet::Node::Configuration.new + @configuration.add_resource(@package) + end + + def setprops(properties) + @provider.stubs(:properties).returns(properties) + end + + def teardown + @configuration.clear(true) + Puppet::Type::Package.clear + end +end + +describe Puppet::Type::Package, "when it should be purged" do + include PackageEvaluationTesting + + before { @package[:ensure] = :purged } + + it "should do nothing if it is :purged" do + @provider.expects(:properties).returns(:ensure => :purged) + @configuration.apply + end + + [:absent, :installed, :present, :latest].each do |state| + it "should purge if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:purge) + @configuration.apply + end + end +end + +describe Puppet::Type::Package, "when it should be absent" do + include PackageEvaluationTesting + + before { @package[:ensure] = :absent } + + [:purged, :absent].each do |state| + it "should do nothing if it is #{state.to_s}" do + @provider.expects(:properties).returns(:ensure => state) + @configuration.apply + end + end + + [:installed, :present, :latest].each do |state| + it "should uninstall if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:uninstall) + @configuration.apply + end + end +end + +describe Puppet::Type::Package, "when it should be present" do + include PackageEvaluationTesting + + before { @package[:ensure] = :present } + + [:present, :latest, "1.0"].each do |state| + it "should do nothing if it is #{state.to_s}" do + @provider.expects(:properties).returns(:ensure => state) + @configuration.apply + end + end + + [:purged, :absent].each do |state| + it "should install if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:install) + @configuration.apply + end + end +end + +describe Puppet::Type::Package, "when it should be latest" do + include PackageEvaluationTesting + + before { @package[:ensure] = :latest } + + [:purged, :absent].each do |state| + it "should upgrade if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:update) + @configuration.apply + end + end + + it "should upgrade if the current version is not equal to the latest version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.stubs(:latest).returns("2.0") + @provider.expects(:update) + @configuration.apply + end + + it "should do nothing if it is equal to the latest version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.stubs(:latest).returns("1.0") + @provider.expects(:update).never + @configuration.apply + end + + it "should do nothing if the provider returns :present as the latest version" do + @provider.stubs(:properties).returns(:ensure => :present) + @provider.stubs(:latest).returns("1.0") + @provider.expects(:update).never + @configuration.apply + end +end + +describe Puppet::Type::Package, "when it should be a specific version" do + include PackageEvaluationTesting + + before { @package[:ensure] = "1.0" } + + [:purged, :absent].each do |state| + it "should install if it is #{state.to_s}" do + @provider.stubs(:properties).returns(:ensure => state) + @provider.expects(:install) + @configuration.apply + end + end + + it "should do nothing if the current version is equal to the desired version" do + @provider.stubs(:properties).returns(:ensure => "1.0") + @provider.expects(:install).never + @configuration.apply + end + + it "should install if the current version is not equal to the specified version" do + @provider.stubs(:properties).returns(:ensure => "2.0") + @provider.expects(:install) + @configuration.apply + end +end diff --git a/test/ral/providers/package.rb b/test/ral/providers/package.rb index a602a4dab..2040d6633 100755 --- a/test/ral/providers/package.rb +++ b/test/ral/providers/package.rb @@ -149,7 +149,7 @@ class TestPackageProvider < Test::Unit::TestCase assert_absent(provider) if Process.uid != 0 - $stderr.puts "Run as root for full package tests" + Puppet.info "Run as root for full package tests" return end diff --git a/test/ral/types/package.rb b/test/ral/types/package.rb deleted file mode 100755 index c8d7d6088..000000000 --- a/test/ral/types/package.rb +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../lib/puppettest' - -require 'puppettest' -require 'puppettest/support/utils' -require 'facter' -require 'mocha' - -$platform = Facter["operatingsystem"].value - -class TestPackages < Test::Unit::TestCase - include PuppetTest::Support::Utils - include PuppetTest::FileTesting - def setup - super - Puppet.type(:package).clear - @type = Puppet::Type.type(:package) - end - - # 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 = @resource.should(:ensure) - end - - def uninstall - self.ensure = :absent - end - - def query - case self.ensure - when :absent, nil: nil - else - {:ensure => self.ensure} - end - end - end - - pkg = nil - assert_nothing_raised do - pkg = @type.create :name => "testing", :provider => :fake - end - assert(pkg, "did not create package") - - current_values = nil - assert_nothing_raised do - current_values = pkg.retrieve - end - - assert_equal(:absent, current_values[pkg.property(:ensure)], - "package not considered missing") - assert_equal(:present, pkg.should(:ensure), - "package did not default to installed") - - assert_events([:package_installed], pkg) - - 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": :up2date - when "Fedora": :yum - when "FreeBSD": :ports - when "OpenBSD": :openbsd - when "Solaris": :sun - end - - unless default = Puppet::Type.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 - - # Make sure we can prefetch and retrieve packages - def test_package_instances - providers = [] - instances = nil - assert_nothing_raised("Could not get package instances") do - instances = @type.instances - end - instances.each do |resource| - # Just do one of each type - next if providers.include?(resource.provider.class) - providers << resource.provider.class - - # We should have data on the resource - assert(resource.exists?, "Listed resource thinks it's absent") - - # Now flush the resource and make sure it clears the property_hash - assert_nothing_raised("Could not flush package") do - resource.flush - end - - assert_equal(:absent, resource.provider.get(:ensure), "Flushing did not empty property hash") - - # And query anew - props = nil - assert_nothing_raised("Could not retrieve package again") do - props = resource.retrieve - end - provider_props = resource.provider.send(:instance_variable_get, "@property_hash") - props.each do |prop, value| - assert_equal(value, provider_props[prop.name], "Query did not return same result as the property_hash for %s" % prop.name) - end - end - end - - # Make sure we can prefetch package information, rather than getting it one package at a time. - def test_prefetch - @type.providers_by_source.each do |provider| - # The yum provider can't be used if you're not root - next if provider.name == :yum && Process.euid != 0 - - # First get a list of packages - list = provider.instances - - packages = {} - list.each do |package| - packages[package.name] = @type.create(:name => package.name, :ensure => :installed) - break if packages.length > 4 - end - - # Now prefetch using that list of packages - assert_nothing_raised("Could not prefetch with %s" % provider.name) do - provider.prefetch(packages) - end - - # And make sure each package is marked as existing, without calling query - packages.each do |name, package| - assert(package.exists?, "Package of type %s not marked present" % provider.name) - package.provider.expects(:query).never - end - end - end - - # #716 - def test_purge_is_not_installed - package = @type.create(:ensure => :installed, :name => "whatever") - - property = package.property(:ensure) - assert(! property.insync?(:purged), "Package in state 'purged' was considered in sync") - end -end - |