diff --git a/lib/puppet/type/package.rb b/lib/puppet/type/package.rb index b1c5de1a1..cfa630582 100644 --- a/lib/puppet/type/package.rb +++ b/lib/puppet/type/package.rb @@ -1,504 +1,500 @@ # Define the different packaging systems. Each package system is implemented # in a module, which then gets used to individually extend each package object. # This allows packages to exist on the same machine using different packaging # systems. require 'puppet/parameter/package_options' require 'puppet/parameter/boolean' module Puppet 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 retrieve their own package files, while others (e.g., rpm and sun) cannot. For those package formats that cannot retrieve their own files, you can use the `source` parameter to point to the correct file. Puppet will automatically guess the packaging format that you are using based on the platform you are on, but you can override it using the `provider` parameter; each provider defines what it requires in order to function, and you must meet those requirements to use a given provider. **Autorequires:** If Puppet is managing the files specified as a package's `adminfile`, `responsefile`, or `source`, the package resource will autorequire those files." feature :reinstallable, "The provider can reinstall packages.", :methods => [:reinstall] feature :installable, "The provider can install packages.", :methods => [:install] feature :uninstallable, "The provider can uninstall packages.", :methods => [:uninstall] feature :upgradeable, "The provider can upgrade to the latest version of a package. This feature is used by specifying `latest` as the desired value for the package.", :methods => [:update, :latest] feature :purgeable, "The provider can purge packages. This generally means that all traces of the package are removed, including existing configuration files. This feature is thus destructive and should be used with the utmost care.", :methods => [:purge] feature :versionable, "The provider is capable of interrogating the package database for installed version(s), and can select which out of a set of available versions of a package to install if asked." feature :holdable, "The provider is capable of placing packages on hold such that they are not automatically upgraded as a result of other package dependencies unless explicit action is taken by a user or another package. Held is considered a superset of installed.", :methods => [:hold] feature :install_options, "The provider accepts options to be passed to the installer command." feature :uninstall_options, "The provider accepts options to be passed to the uninstaller command." feature :package_settings, "The provider accepts package_settings to be ensured for the given package. The meaning and format of these settings is provider-specific.", :methods => [:package_settings_insync?, :package_settings, :package_settings=] feature :virtual_packages, "The provider accepts virtual package names for install and uninstall." ensurable do desc <<-EOT What state the package should be in. On packaging systems that can retrieve new packages on their own, you can choose which package to retrieve by specifying a version number or `latest` as the ensure value. On packaging systems that manage configuration files separately from "normal" system files, you can uninstall config files by specifying `purged` as the ensure value. This defaults to `installed`. EOT attr_accessor :latest newvalue(:present, :event => :package_installed) do provider.install end newvalue(:absent, :event => :package_removed) do provider.uninstall end newvalue(:purged, :event => :package_purged, :required_features => :purgeable) do provider.purge end newvalue(:held, :event => :package_held, :required_features => :holdable) do provider.hold end # Alias the 'present' value. aliasvalue(:installed, :present) newvalue(:latest, :required_features => :upgradeable) do # Because yum always exits with a 0 exit code, there's a retrieve # in the "install" method. So, check the current state now, # to compare against later. current = self.retrieve begin provider.update rescue => detail self.fail Puppet::Error, "Could not update: #{detail}", detail end if current == :absent :package_installed else :package_changed end end newvalue(/./, :required_features => :versionable) do begin provider.install rescue => detail self.fail Puppet::Error, "Could not update: #{detail}", detail end if self.retrieve == :absent :package_installed else :package_changed end end defaultto :installed # Override the parent method, because we've got all kinds of # funky definitions of 'in sync'. def insync?(is) @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 return true unless [:absent, :purged, :held].include?(is) when :latest # Short-circuit packages that are not present 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" else begin @latest = provider.latest @lateststamp = Time.now.to_i rescue => detail error = Puppet::Error.new("Could not get latest version: #{detail}") error.set_backtrace(detail.backtrace) raise error end end case when is.is_a?(Array) && is.include?(@latest) return true when is == @latest return true when is == :present # This will only happen on retarded packaging systems # that can't query versions. return true else self.debug "#{@resource.name} #{is.inspect} is installed, latest is #{@latest.inspect}" end when :absent return true if is == :absent or is == :purged when :purged return true if is == :purged # this handles version number matches and # supports providers that can have multiple versions installed when *Array(is) return true end } false end # This retrieves the current state. LAK: I think this method is unused. def retrieve provider.properties[:ensure] end # Provide a bit more information when logging upgrades. def should_to_s(newvalue = @should) if @latest @latest.to_s else super(newvalue) end end end newparam(:name) do desc "The package name. This is the name that the packaging system uses internally, which is sometimes (especially on Solaris) a name that is basically useless to humans. If you want to abstract package installation, then you can use aliases to provide a common name to packages: # In the 'openssl' class $ssl = $operatingsystem ? { solaris => SMCossl, default => openssl } # It is not an error to set an alias to the same value as the # object name. package { $ssl: ensure => installed, alias => openssl } . etc. . $ssh = $operatingsystem ? { solaris => SMCossh, default => openssh } # Use the alias to specify a dependency, rather than # having another selector to figure it out again. package { $ssh: ensure => installed, alias => openssh, require => Package[openssl] } " isnamevar validate do |value| if !value.is_a?(String) raise ArgumentError, "Name must be a String not #{value.class}" end end end newproperty(:package_settings, :required_features=>:package_settings) do desc "Settings that can change the contents or configuration of a package. The formatting and effects of package_settings are provider-specific; any provider that implements them must explain how to use them in its documentation. (Our general expectation is that if a package is installed but its settings are out of sync, the provider should re-install that package with the desired settings.) An example of how package_settings could be used is FreeBSD's port build options --- a future version of the provider could accept a hash of options, and would reinstall the port if the installed version lacked the correct settings. package { 'www/apache22': package_settings => { 'SUEXEC' => false } } Again, check the documentation of your platform's package provider to see the actual usage." validate do |value| if provider.respond_to?(:package_settings_validate) provider.package_settings_validate(value) else super(value) end end munge do |value| if provider.respond_to?(:package_settings_munge) provider.package_settings_munge(value) else super(value) end end def insync?(is) provider.package_settings_insync?(should, is) end def should_to_s(newvalue) if provider.respond_to?(:package_settings_should_to_s) provider.package_settings_should_to_s(should, newvalue) else super(newvalue) end end def is_to_s(currentvalue) if provider.respond_to?(:package_settings_is_to_s) provider.package_settings_is_to_s(should, currentvalue) else super(currentvalue) end end def change_to_s(currentvalue, newvalue) if provider.respond_to?(:package_settings_change_to_s) provider.package_settings_change_to_s(currentvalue, newvalue) else super(currentvalue,newvalue) end end end newparam(:source) do desc "Where to find the actual package. This must be a local file (or on a network file system) or a URL that your specific packaging type understands; Puppet will not retrieve files for you, although you can manage packages as `file` resources." validate do |value| provider.validate_source(value) end end newparam(:instance) do desc "A read-only parameter set by the package." end newparam(:status) do desc "A read-only parameter set by the package." end newparam(:adminfile) do desc "A file containing package defaults for installing packages. This is currently only used on Solaris. The value will be validated according to system rules, which in the case of Solaris means that it should either be a fully qualified path or it should be in `/var/sadm/install/admin`." end newparam(:responsefile) do desc "A file containing any necessary answers to questions asked by the package. This is currently used on Solaris and Debian. The value will be validated according to system rules, but it should generally be a fully qualified path." end newparam(:configfiles) do desc "Whether configfiles should be kept or replaced. Most packages types do not support this parameter. Defaults to `keep`." defaultto :keep newvalues(:keep, :replace) end newparam(:category) do desc "A read-only parameter set by the package." end newparam(:platform) do desc "A read-only parameter set by the package." end newparam(:root) do desc "A read-only parameter set by the package." end newparam(:vendor) do desc "A read-only parameter set by the package." end newparam(:description) do desc "A read-only parameter set by the package." end newparam(:allowcdrom) do desc "Tells apt to allow cdrom sources in the sources.list file. Normally apt will bail if you try this." newvalues(:true, :false) end newparam(:flavor) do desc "OpenBSD supports 'flavors', which are further specifications for which type of package you want." end newparam(:install_options, :parent => Puppet::Parameter::PackageOptions, :required_features => :install_options) do desc <<-EOT An array of additional options to pass when installing a package. These options are package-specific, and should be documented by the software vendor. One commonly implemented option is `INSTALLDIR`: package { 'mysql': ensure => installed, source => 'N:/packages/mysql-5.5.16-winx64.msi', install_options => [ '/S', { 'INSTALLDIR' => 'C:\\mysql-5.5' } ], } Each option in the array can either be a string or a hash, where each key and value pair are interpreted in a provider specific way. Each option will automatically be quoted when passed to the install command. On Windows, this is the **only** place in Puppet where backslash separators should be used. Note that backslashes in double-quoted strings _must_ be double-escaped and backslashes in single-quoted strings _may_ be double-escaped. EOT end newparam(:uninstall_options, :parent => Puppet::Parameter::PackageOptions, :required_features => :uninstall_options) do desc <<-EOT An array of additional options to pass when uninstalling a package. These options are package-specific, and should be documented by the software vendor. For example: package { 'VMware Tools': ensure => absent, uninstall_options => [ { 'REMOVE' => 'Sync,VSS' } ], } Each option in the array can either be a string or a hash, where each key and value pair are interpreted in a provider specific way. Each option will automatically be quoted when passed to the uninstall command. On Windows, this is the **only** place in Puppet where backslash separators should be used. Note that backslashes in double-quoted strings _must_ be double-escaped and backslashes in single-quoted strings _may_ be double-escaped. EOT end newparam(:allow_virtual, :boolean => true, :parent => Puppet::Parameter::Boolean, :required_features => :virtual_packages) do desc 'Specifies if virtual package names are allowed for install and uninstall.' - # In a future release, this should be defaulted to true and the below deprecation warning removed - defaultto do - Puppet.deprecation_warning('The package type\'s allow_virtual parameter will be changing its default value from false to true in a future release. If you do not want to allow virtual packages, please explicitly set allow_virtual to false.') unless value - false - end + defaultto true end autorequire(:file) do autos = [] [:responsefile, :adminfile].each { |param| if val = self[param] autos << val end } if source = self[:source] and absolute_path?(source) autos << source end autos end # This only exists for testing. def clear if obj = @parameters[:ensure] obj.latest = nil end end # The 'query' method returns a hash of info if the package # exists and returns nil if it does not. def exists? @provider.get(:ensure) != :absent end def present?(current_values) super && current_values[:ensure] != :purged end # This parameter exists to ensure backwards compatibility is preserved. # See https://github.com/puppetlabs/puppet/pull/2614 for discussion. # If/when a metaparameter for controlling how arbitrary resources respond # to refreshing is created, that will supersede this, and this will be # deprecated. newparam(:reinstall_on_refresh) do desc "Whether this resource should respond to refresh events (via `subscribe`, `notify`, or the `~>` arrow) by reinstalling the package. Only works for providers that support the `reinstallable` feature. This is useful for source-based distributions, where you may want to recompile a package if the build options change. If you use this, be careful of notifying classes when you want to restart services. If the class also contains a refreshable package, doing so could cause unnecessary re-installs. Defaults to `false`." newvalues(:true, :false) defaultto :false end # When a refresh event is triggered, calls reinstall on providers # that support the reinstall_on_refresh parameter. def refresh if provider.reinstallable? && @parameters[:reinstall_on_refresh].value == :true && @parameters[:ensure].value != :purged && @parameters[:ensure].value != :absent && @parameters[:ensure].value != :held provider.reinstall end end end end diff --git a/spec/unit/provider/package/rpm_spec.rb b/spec/unit/provider/package/rpm_spec.rb index 24449ef9d..dd1983d32 100755 --- a/spec/unit/provider/package/rpm_spec.rb +++ b/spec/unit/provider/package/rpm_spec.rb @@ -1,471 +1,484 @@ #! /usr/bin/env ruby require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:rpm) describe provider_class do let (:packages) do <<-RPM_OUTPUT cracklib-dicts 0 2.8.9 3.3 x86_64 basesystem 0 8.0 5.1.1.el5.centos noarch chkconfig 0 1.3.30.2 2.el5 x86_64 myresource 0 1.2.3.4 5.el4 noarch mysummaryless 0 1.2.3.4 5.el4 noarch RPM_OUTPUT end let(:resource_name) { 'myresource' } let(:resource) do Puppet::Type.type(:package).new( :name => resource_name, :ensure => :installed, :provider => 'rpm' ) end let(:provider) do provider = provider_class.new provider.resource = resource provider end let(:nevra_format) { %Q{%{NAME} %|EPOCH?{%{EPOCH}}:{0}| %{VERSION} %{RELEASE} %{ARCH}\\n} } let(:execute_options) do {:failonfail => true, :combine => true, :custom_environment => {}} end let(:rpm_version) { "RPM version 5.0.0\n" } before(:each) do Puppet::Util.stubs(:which).with("rpm").returns("/bin/rpm") provider_class.stubs(:which).with("rpm").returns("/bin/rpm") provider_class.instance_variable_set("@current_version", nil) Puppet::Type::Package::ProviderRpm.expects(:execute).with(["/bin/rpm", "--version"]).returns(rpm_version).at_most_once Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "--version"], execute_options).returns(rpm_version).at_most_once end describe 'provider features' do it { should be_versionable } it { should be_install_options } it { should be_uninstall_options } it { should be_virtual_packages } end describe "self.instances" do describe "with a modern version of RPM" do it "includes all the modern flags" do Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf '#{nevra_format}'").yields(packages) installed_packages = provider_class.instances end end describe "with a version of RPM < 4.1" do let(:rpm_version) { "RPM version 4.0.2\n" } it "excludes the --nosignature flag" do Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nodigest --qf '#{nevra_format}'").yields(packages) installed_packages = provider_class.instances end end describe "with a version of RPM < 4.0.2" do let(:rpm_version) { "RPM version 3.0.5\n" } it "excludes the --nodigest flag" do Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --qf '#{nevra_format}'").yields(packages) installed_packages = provider_class.instances end end it "returns an array of packages" do Puppet::Util::Execution.expects(:execpipe).with("/bin/rpm -qa --nosignature --nodigest --qf '#{nevra_format}'").yields(packages) installed_packages = provider_class.instances expect(installed_packages[0].properties).to eq( { :provider => :rpm, :name => "cracklib-dicts", :epoch => "0", :version => "2.8.9", :release => "3.3", :arch => "x86_64", :ensure => "2.8.9-3.3", } ) expect(installed_packages[1].properties).to eq( { :provider => :rpm, :name => "basesystem", :epoch => "0", :version => "8.0", :release => "5.1.1.el5.centos", :arch => "noarch", :ensure => "8.0-5.1.1.el5.centos", } ) expect(installed_packages[2].properties).to eq( { :provider => :rpm, :name => "chkconfig", :epoch => "0", :version => "1.3.30.2", :release => "2.el5", :arch => "x86_64", :ensure => "1.3.30.2-2.el5", } ) expect(installed_packages.last.properties).to eq( { :provider => :rpm, :name => "mysummaryless", :epoch => "0", :version => "1.2.3.4", :release => "5.el4", :arch => "noarch", :ensure => "1.2.3.4-5.el4", } ) end end describe "#install" do let(:resource) do Puppet::Type.type(:package).new( :name => 'myresource', :ensure => :installed, :source => '/path/to/package' ) end describe "when not already installed" do it "only includes the '-i' flag" do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-i"], '/path/to/package'], execute_options) provider.install end end describe "when installed with options" do let(:resource) do Puppet::Type.type(:package).new( :name => resource_name, :ensure => :installed, :provider => 'rpm', :source => '/path/to/package', :install_options => ['-D', {'--test' => 'value'}, '-Q'] ) end it "includes the options" do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-i", "-D", "--test=value", "-Q"], '/path/to/package'], execute_options) provider.install end end describe "when an older version is installed" do before(:each) do # Force the provider to think a version of the package is already installed # This is real hacky. I'm sorry. --jeffweiss 25 Jan 2013 provider.instance_variable_get('@property_hash')[:ensure] = '1.2.3.3' end it "includes the '-U --oldpackage' flags" do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-U", "--oldpackage"], '/path/to/package'], execute_options) provider.install end end end describe "#latest" do it "retrieves version string after querying rpm for version from source file" do resource.expects(:[]).with(:source).returns('source-string') Puppet::Util::Execution.expects(:execfail).with(["/bin/rpm", "-q", "--qf", nevra_format, "-p", "source-string"], Puppet::Error).returns("myresource 0 1.2.3.4 5.el4 noarch\n") expect(provider.latest).to eq("1.2.3.4-5.el4") end end describe "#uninstall" do let(:resource) do Puppet::Type.type(:package).new( :name => 'myresource', :ensure => :installed ) end describe "on a modern RPM" do before(:each) do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "myresource", '--nosignature', '--nodigest', "--qf", nevra_format], execute_options).returns("myresource 0 1.2.3.4 5.el4 noarch\n") end let(:rpm_version) { "RPM version 4.10.0\n" } it "includes the architecture in the package name" do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-e"], 'myresource-1.2.3.4-5.el4.noarch'], execute_options).returns('').at_most_once provider.uninstall end end describe "on an ancient RPM" do before(:each) do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "myresource", '', '', '--qf', nevra_format], execute_options).returns("myresource 0 1.2.3.4 5.el4 noarch\n") end let(:rpm_version) { "RPM version 3.0.6\n" } it "excludes the architecture from the package name" do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-e"], 'myresource-1.2.3.4-5.el4'], execute_options).returns('').at_most_once provider.uninstall end end describe "when uninstalled with options" do before(:each) do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", "myresource", '--nosignature', '--nodigest', "--qf", nevra_format], execute_options).returns("myresource 0 1.2.3.4 5.el4 noarch\n") end let(:resource) do Puppet::Type.type(:package).new( :name => resource_name, :ensure => :absent, :provider => 'rpm', :uninstall_options => ['--nodeps'] ) end it "includes the options" do Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", ["-e", "--nodeps"], 'myresource-1.2.3.4-5.el4.noarch'], execute_options) provider.uninstall end end end describe "parsing" do def parser_test(rpm_output_string, gold_hash, number_of_debug_logs = 0) Puppet.expects(:debug).times(number_of_debug_logs) Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "-q", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format], execute_options).returns(rpm_output_string) expect(provider.query).to eq(gold_hash) end let(:resource_name) { 'name' } let('delimiter') { ':DESC:' } let(:package_hash) do { :name => 'name', :epoch => 'epoch', :version => 'version', :release => 'release', :arch => 'arch', :provider => :rpm, :ensure => 'version-release', } end let(:line) { 'name epoch version release arch' } ['name', 'epoch', 'version', 'release', 'arch'].each do |field| it "still parses if #{field} is replaced by delimiter" do parser_test( line.gsub(field, delimiter), package_hash.merge( field.to_sym => delimiter, :ensure => 'version-release'.gsub(field, delimiter) ) ) end end it "does not fail if line is unparseable, but issues a debug log" do parser_test('bad data', {}, 1) end - it "does not log or fail if rpm returns package not found" do - Puppet.expects(:debug).never - expected_args = ["/bin/rpm", "-q", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format] - Puppet::Util::Execution.expects(:execute).with(expected_args, execute_options).raises Puppet::ExecutionFailure.new("package #{resource_name} is not installed") - expect(provider.query).to be_nil + describe "when the package is not found" do + before do + Puppet.expects(:debug).never + expected_args = ["/bin/rpm", "-q", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format] + Puppet::Util::Execution.expects(:execute).with(expected_args, execute_options).raises Puppet::ExecutionFailure.new("package #{resource_name} is not installed") + end + + it "does not log or fail if allow_virtual is false" do + resource[:allow_virtual] = false + expect(provider.query).to be_nil + end + + it "does not log or fail if allow_virtual is true" do + resource[:allow_virtual] = true + expected_args = ['/bin/rpm', '-q', resource_name, '--nosignature', '--nodigest', '--qf', nevra_format, '--whatprovides'] + Puppet::Util::Execution.expects(:execute).with(expected_args, execute_options).raises Puppet::ExecutionFailure.new("package #{resource_name} is not provided") + expect(provider.query).to be_nil + end end it "parses virtual package" do provider.resource[:allow_virtual] = true expected_args = ["/bin/rpm", "-q", resource_name, "--nosignature", "--nodigest", "--qf", nevra_format] Puppet::Util::Execution.expects(:execute).with(expected_args, execute_options).raises Puppet::ExecutionFailure.new("package #{resource_name} is not installed") Puppet::Util::Execution.expects(:execute).with(expected_args + ["--whatprovides"], execute_options).returns "myresource 0 1.2.3.4 5.el4 noarch\n" expect(provider.query).to eq({ :name => "myresource", :epoch => "0", :version => "1.2.3.4", :release => "5.el4", :arch => "noarch", :provider => :rpm, :ensure => "1.2.3.4-5.el4" }) end end describe "#install_options" do it "returns nil by default" do expect(provider.install_options).to eq(nil) end it "returns install_options when set" do provider.resource[:install_options] = ['-n'] expect(provider.install_options).to eq(['-n']) end it "returns multiple install_options when set" do provider.resource[:install_options] = ['-L', '/opt/puppet'] expect(provider.install_options).to eq(['-L', '/opt/puppet']) end it 'returns install_options when set as hash' do provider.resource[:install_options] = [{ '-Darch' => 'vax' }] expect(provider.install_options).to eq(['-Darch=vax']) end it 'returns install_options when an array with hashes' do provider.resource[:install_options] = [ '-L', { '-Darch' => 'vax' }] expect(provider.install_options).to eq(['-L', '-Darch=vax']) end end describe "#uninstall_options" do it "returns nil by default" do expect(provider.uninstall_options).to eq(nil) end it "returns uninstall_options when set" do provider.resource[:uninstall_options] = ['-n'] expect(provider.uninstall_options).to eq(['-n']) end it "returns multiple uninstall_options when set" do provider.resource[:uninstall_options] = ['-L', '/opt/puppet'] expect(provider.uninstall_options).to eq(['-L', '/opt/puppet']) end it 'returns uninstall_options when set as hash' do provider.resource[:uninstall_options] = [{ '-Darch' => 'vax' }] expect(provider.uninstall_options).to eq(['-Darch=vax']) end it 'returns uninstall_options when an array with hashes' do provider.resource[:uninstall_options] = [ '-L', { '-Darch' => 'vax' }] expect(provider.uninstall_options).to eq(['-L', '-Darch=vax']) end end describe ".nodigest" do { '4.0' => nil, '4.0.1' => nil, '4.0.2' => '--nodigest', '4.0.3' => '--nodigest', '4.1' => '--nodigest', '5' => '--nodigest', }.each do |version, expected| describe "when current version is #{version}" do it "returns #{expected.inspect}" do provider_class.stubs(:current_version).returns(version) expect(provider_class.nodigest).to eq(expected) end end end end describe ".nosignature" do { '4.0.3' => nil, '4.1' => '--nosignature', '4.1.1' => '--nosignature', '4.2' => '--nosignature', '5' => '--nosignature', }.each do |version, expected| describe "when current version is #{version}" do it "returns #{expected.inspect}" do provider_class.stubs(:current_version).returns(version) expect(provider_class.nosignature).to eq(expected) end end end end describe 'version comparison' do # test cases munged directly from rpm's own # tests/rpmvercmp.at it { provider.rpmvercmp("1.0", "1.0").should == 0 } it { provider.rpmvercmp("1.0", "2.0").should == -1 } it { provider.rpmvercmp("2.0", "1.0").should == 1 } it { provider.rpmvercmp("2.0.1", "2.0.1").should == 0 } it { provider.rpmvercmp("2.0", "2.0.1").should == -1 } it { provider.rpmvercmp("2.0.1", "2.0").should == 1 } it { provider.rpmvercmp("2.0.1a", "2.0.1a").should == 0 } it { provider.rpmvercmp("2.0.1a", "2.0.1").should == 1 } it { provider.rpmvercmp("2.0.1", "2.0.1a").should == -1 } it { provider.rpmvercmp("5.5p1", "5.5p1").should == 0 } it { provider.rpmvercmp("5.5p1", "5.5p2").should == -1 } it { provider.rpmvercmp("5.5p2", "5.5p1").should == 1 } it { provider.rpmvercmp("5.5p10", "5.5p10").should == 0 } it { provider.rpmvercmp("5.5p1", "5.5p10").should == -1 } it { provider.rpmvercmp("5.5p10", "5.5p1").should == 1 } it { provider.rpmvercmp("10xyz", "10.1xyz").should == -1 } it { provider.rpmvercmp("10.1xyz", "10xyz").should == 1 } it { provider.rpmvercmp("xyz10", "xyz10").should == 0 } it { provider.rpmvercmp("xyz10", "xyz10.1").should == -1 } it { provider.rpmvercmp("xyz10.1", "xyz10").should == 1 } it { provider.rpmvercmp("xyz.4", "xyz.4").should == 0 } it { provider.rpmvercmp("xyz.4", "8").should == -1 } it { provider.rpmvercmp("8", "xyz.4").should == 1 } it { provider.rpmvercmp("xyz.4", "2").should == -1 } it { provider.rpmvercmp("2", "xyz.4").should == 1 } it { provider.rpmvercmp("5.5p2", "5.6p1").should == -1 } it { provider.rpmvercmp("5.6p1", "5.5p2").should == 1 } it { provider.rpmvercmp("5.6p1", "6.5p1").should == -1 } it { provider.rpmvercmp("6.5p1", "5.6p1").should == 1 } it { provider.rpmvercmp("6.0.rc1", "6.0").should == 1 } it { provider.rpmvercmp("6.0", "6.0.rc1").should == -1 } it { provider.rpmvercmp("10b2", "10a1").should == 1 } it { provider.rpmvercmp("10a2", "10b2").should == -1 } it { provider.rpmvercmp("1.0aa", "1.0aa").should == 0 } it { provider.rpmvercmp("1.0a", "1.0aa").should == -1 } it { provider.rpmvercmp("1.0aa", "1.0a").should == 1 } it { provider.rpmvercmp("10.0001", "10.0001").should == 0 } it { provider.rpmvercmp("10.0001", "10.1").should == 0 } it { provider.rpmvercmp("10.1", "10.0001").should == 0 } it { provider.rpmvercmp("10.0001", "10.0039").should == -1 } it { provider.rpmvercmp("10.0039", "10.0001").should == 1 } it { provider.rpmvercmp("4.999.9", "5.0").should == -1 } it { provider.rpmvercmp("5.0", "4.999.9").should == 1 } it { provider.rpmvercmp("20101121", "20101121").should == 0 } it { provider.rpmvercmp("20101121", "20101122").should == -1 } it { provider.rpmvercmp("20101122", "20101121").should == 1 } it { provider.rpmvercmp("2_0", "2_0").should == 0 } it { provider.rpmvercmp("2.0", "2_0").should == 0 } it { provider.rpmvercmp("2_0", "2.0").should == 0 } it { provider.rpmvercmp("a", "a").should == 0 } it { provider.rpmvercmp("a+", "a+").should == 0 } it { provider.rpmvercmp("a+", "a_").should == 0 } it { provider.rpmvercmp("a_", "a+").should == 0 } it { provider.rpmvercmp("+a", "+a").should == 0 } it { provider.rpmvercmp("+a", "_a").should == 0 } it { provider.rpmvercmp("_a", "+a").should == 0 } it { provider.rpmvercmp("+_", "+_").should == 0 } it { provider.rpmvercmp("_+", "+_").should == 0 } it { provider.rpmvercmp("_+", "_+").should == 0 } it { provider.rpmvercmp("+", "_").should == 0 } it { provider.rpmvercmp("_", "+").should == 0 } it { provider.rpmvercmp("1.0~rc1", "1.0~rc1").should == 0 } it { provider.rpmvercmp("1.0~rc1", "1.0").should == -1 } it { provider.rpmvercmp("1.0", "1.0~rc1").should == 1 } it { provider.rpmvercmp("1.0~rc1", "1.0~rc2").should == -1 } it { provider.rpmvercmp("1.0~rc2", "1.0~rc1").should == 1 } it { provider.rpmvercmp("1.0~rc1~git123", "1.0~rc1~git123").should == 0 } it { provider.rpmvercmp("1.0~rc1~git123", "1.0~rc1").should == -1 } it { provider.rpmvercmp("1.0~rc1", "1.0~rc1~git123").should == 1 } it { provider.rpmvercmp("1.0~rc1", "1.0arc1").should == -1 } # non-upstream test cases it { provider.rpmvercmp("405", "406").should == -1 } it { provider.rpmvercmp("1", "0").should == 1 } end end diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb index 0e88270ed..9b6b69d71 100755 --- a/spec/unit/provider/package/yum_spec.rb +++ b/spec/unit/provider/package/yum_spec.rb @@ -1,449 +1,446 @@ #! /usr/bin/env ruby require 'spec_helper' provider_class = Puppet::Type.type(:package).provider(:yum) describe provider_class do let(:name) { 'mypackage' } let(:resource) do Puppet::Type.type(:package).new( :name => name, :ensure => :installed, :provider => 'yum' ) end let(:provider) do provider = provider_class.new provider.resource = resource provider end before do provider.stubs(:yum).returns 'yum' provider.stubs(:rpm).returns 'rpm' provider.stubs(:get).with(:version).returns '1' provider.stubs(:get).with(:release).returns '1' provider.stubs(:get).with(:arch).returns 'i386' end describe 'provider features' do it { should be_versionable } it { should be_install_options } it { should be_virtual_packages } end # provider should repond to the following methods [:install, :latest, :update, :purge, :install_options].each do |method| it "should have a(n) #{method}" do provider.should respond_to(method) end end describe 'package evr parsing' do it 'should parse full simple evr' do v = provider.yum_parse_evr('0:1.2.3-4.el5') v[:epoch].should == '0' v[:version].should == '1.2.3' v[:release].should == '4.el5' end it 'should parse version only' do v = provider.yum_parse_evr('1.2.3') v[:epoch].should == '0' v[:version].should == '1.2.3' v[:release].should == nil end it 'should parse version-release' do v = provider.yum_parse_evr('1.2.3-4.5.el6') v[:epoch].should == '0' v[:version].should == '1.2.3' v[:release].should == '4.5.el6' end it 'should parse release with git hash' do v = provider.yum_parse_evr('1.2.3-4.1234aefd') v[:epoch].should == '0' v[:version].should == '1.2.3' v[:release].should == '4.1234aefd' end it 'should parse single integer versions' do v = provider.yum_parse_evr('12345') v[:epoch].should == '0' v[:version].should == '12345' v[:release].should == nil end it 'should parse text in the epoch to 0' do v = provider.yum_parse_evr('foo0:1.2.3-4') v[:epoch].should == '0' v[:version].should == '1.2.3' v[:release].should == '4' end it 'should parse revisions with text' do v = provider.yum_parse_evr('1.2.3-SNAPSHOT20140107') v[:epoch].should == '0' v[:version].should == '1.2.3' v[:release].should == 'SNAPSHOT20140107' end # test cases for PUP-682 it 'should parse revisions with text and numbers' do v = provider.yum_parse_evr('2.2-SNAPSHOT20121119105647') v[:epoch].should == '0' v[:version].should == '2.2' v[:release].should == 'SNAPSHOT20121119105647' end end describe 'yum evr comparison' do # currently passing tests it 'should evaluate identical version-release as equal' do v = provider.yum_compareEVR({:epoch => '0', :version => '1.2.3', :release => '1.el5'}, {:epoch => '0', :version => '1.2.3', :release => '1.el5'}) v.should == 0 end it 'should evaluate identical version as equal' do v = provider.yum_compareEVR({:epoch => '0', :version => '1.2.3', :release => nil}, {:epoch => '0', :version => '1.2.3', :release => nil}) v.should == 0 end it 'should evaluate identical version but older release as less' do v = provider.yum_compareEVR({:epoch => '0', :version => '1.2.3', :release => '1.el5'}, {:epoch => '0', :version => '1.2.3', :release => '2.el5'}) v.should == -1 end it 'should evaluate identical version but newer release as greater' do v = provider.yum_compareEVR({:epoch => '0', :version => '1.2.3', :release => '3.el5'}, {:epoch => '0', :version => '1.2.3', :release => '2.el5'}) v.should == 1 end it 'should evaluate a newer epoch as greater' do v = provider.yum_compareEVR({:epoch => '1', :version => '1.2.3', :release => '4.5'}, {:epoch => '0', :version => '1.2.3', :release => '4.5'}) v.should == 1 end # these tests describe PUP-1244 logic yet to be implemented it 'should evaluate any version as equal to the same version followed by release' do v = provider.yum_compareEVR({:epoch => '0', :version => '1.2.3', :release => nil}, {:epoch => '0', :version => '1.2.3', :release => '2.el5'}) v.should == 0 end # test cases for PUP-682 it 'should evaluate same-length numeric revisions numerically' do provider.yum_compareEVR({:epoch => '0', :version => '2.2', :release => '405'}, {:epoch => '0', :version => '2.2', :release => '406'}).should == -1 end end describe 'yum version segment comparison' do it 'should treat two nil values as equal' do v = provider.compare_values(nil, nil) v.should == 0 end it 'should treat a nil value as less than a non-nil value' do v = provider.compare_values(nil, '0') v.should == -1 end it 'should treat a non-nil value as greater than a nil value' do v = provider.compare_values('0', nil) v.should == 1 end it 'should pass two non-nil values on to rpmvercmp' do provider.stubs(:rpmvercmp) { 0 } provider.expects(:rpmvercmp).with('s1', 's2') provider.compare_values('s1', 's2') end end describe 'when installing' do before(:each) do Puppet::Util.stubs(:which).with("rpm").returns("/bin/rpm") provider.stubs(:which).with("rpm").returns("/bin/rpm") Puppet::Util::Execution.expects(:execute).with(["/bin/rpm", "--version"], {:combine => true, :custom_environment => {}, :failonfail => true}).returns("4.10.1\n").at_most_once end it 'should call yum install for :installed' do resource.stubs(:should).with(:ensure).returns :installed - provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :list, name) provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, name) provider.install end it 'should use :install to update' do provider.expects(:install) provider.update end it 'should be able to set version' do version = '1.2' resource[:ensure] = version - provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :list, name) provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, "#{name}-#{version}") provider.stubs(:query).returns :ensure => version provider.install end it 'should handle partial versions specified' do version = '1.3.4' resource[:ensure] = version provider.stubs(:query).returns :ensure => '1.3.4-1.el6' provider.install end it 'should be able to downgrade' do current_version = '1.2' version = '1.0' resource[:ensure] = '1.0' provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :downgrade, "#{name}-#{version}") provider.stubs(:query).returns(:ensure => current_version).then.returns(:ensure => version) provider.install end it 'should accept install options' do resource[:ensure] = :installed resource[:install_options] = ['-t', {'-x' => 'expackage'}] - provider.expects(:yum).with('-d', '0', '-e', '0', '-y', ['-t', '-x=expackage'], :list, name) provider.expects(:yum).with('-d', '0', '-e', '0', '-y', ['-t', '-x=expackage'], :install, name) provider.install end it 'allow virtual packages' do resource[:ensure] = :installed resource[:allow_virtual] = true provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :list, name).never provider.expects(:yum).with('-d', '0', '-e', '0', '-y', :install, name) provider.install end end describe 'when uninstalling' do it 'should use erase to purge' do provider.expects(:yum).with('-y', :erase, name) provider.purge end end it 'should be versionable' do provider.should be_versionable end describe 'determining the latest version available for a package' do it "passes the value of enablerepo install_options when querying" do resource[:install_options] = [ {'--enablerepo' => 'contrib'}, {'--enablerepo' => 'centosplus'}, ] provider.stubs(:properties).returns({:ensure => '3.4.5'}) described_class.expects(:latest_package_version).with(name, ['contrib', 'centosplus'], []) provider.latest end it "passes the value of disablerepo install_options when querying" do resource[:install_options] = [ {'--disablerepo' => 'updates'}, {'--disablerepo' => 'centosplus'}, ] provider.stubs(:properties).returns({:ensure => '3.4.5'}) described_class.expects(:latest_package_version).with(name, [], ['updates', 'centosplus']) provider.latest end describe 'and a newer version is not available' do before :each do described_class.stubs(:latest_package_version).with(name, [], []).returns nil end it 'raises an error the package is not installed' do provider.stubs(:properties).returns({:ensure => :absent}) expect { provider.latest }.to raise_error(Puppet::DevError, 'Tried to get latest on a missing package') end it 'returns version of the currently installed package' do provider.stubs(:properties).returns({:ensure => '3.4.5'}) provider.latest.should == '3.4.5' end end describe 'and a newer version is available' do let(:latest_version) do { :name => name, :epoch => '1', :version => '2.3.4', :release => '5', :arch => 'i686', } end it 'includes the epoch in the version string' do described_class.stubs(:latest_package_version).with(name, [], []).returns(latest_version) provider.latest.should == '1:2.3.4-5' end end end describe "lazy loading of latest package versions" do before { described_class.clear } after { described_class.clear } let(:mypackage_version) do { :name => name, :epoch => '1', :version => '2.3.4', :release => '5', :arch => 'i686', } end let(:mypackage_newerversion) do { :name => name, :epoch => '1', :version => '4.5.6', :release => '7', :arch => 'i686', } end let(:latest_versions) { {name => [mypackage_version]} } let(:enabled_versions) { {name => [mypackage_newerversion]} } it "returns the version hash if the package was found" do described_class.expects(:fetch_latest_versions).with([], []).once.returns(latest_versions) version = described_class.latest_package_version(name, [], []) expect(version).to eq(mypackage_version) end it "is nil if the package was not found in the query" do described_class.expects(:fetch_latest_versions).with([], []).once.returns(latest_versions) version = described_class.latest_package_version('nopackage', [], []) expect(version).to be_nil end it "caches the package list and reuses that for subsequent queries" do described_class.expects(:fetch_latest_versions).with([], []).once.returns(latest_versions) 2.times { version = described_class.latest_package_version(name, [], []) expect(version).to eq mypackage_version } end it "caches separate lists for each combination of 'enablerepo' and 'disablerepo'" do described_class.expects(:fetch_latest_versions).with([], []).once.returns(latest_versions) described_class.expects(:fetch_latest_versions).with(['enabled'], ['disabled']).once.returns(enabled_versions) 2.times { version = described_class.latest_package_version(name, [], []) expect(version).to eq mypackage_version } 2.times { version = described_class.latest_package_version(name, ['enabled'], ['disabled']) expect(version).to eq(mypackage_newerversion) } end end describe "querying for the latest version of all packages" do let(:yumhelper_single_arch) do <<-YUMHELPER_OUTPUT * base: centos.tcpdiag.net * extras: centos.mirrors.hoobly.com * updates: mirrors.arsc.edu _pkg nss-tools 0 3.14.3 4.el6_4 x86_64 _pkg pixman 0 0.26.2 5.el6_4 x86_64 _pkg myresource 0 1.2.3.4 5.el4 noarch _pkg mysummaryless 0 1.2.3.4 5.el4 noarch YUMHELPER_OUTPUT end let(:yumhelper_multi_arch) do yumhelper_single_arch + <<-YUMHELPER_OUTPUT _pkg nss-tools 0 3.14.3 4.el6_4 i386 _pkg pixman 0 0.26.2 5.el6_4 i386 YUMHELPER_OUTPUT end it "creates an entry for each line that's prefixed with '_pkg'" do described_class.expects(:python).with([described_class::YUMHELPER]).returns(yumhelper_single_arch) entries = described_class.fetch_latest_versions([], []) expect(entries.keys).to include 'nss-tools' expect(entries.keys).to include 'pixman' expect(entries.keys).to include 'myresource' expect(entries.keys).to include 'mysummaryless' end it "creates an entry for each package name and architecture" do described_class.expects(:python).with([described_class::YUMHELPER]).returns(yumhelper_single_arch) entries = described_class.fetch_latest_versions([], []) expect(entries.keys).to include 'nss-tools.x86_64' expect(entries.keys).to include 'pixman.x86_64' expect(entries.keys).to include 'myresource.noarch' expect(entries.keys).to include 'mysummaryless.noarch' end it "stores multiple entries if a package is build for multiple architectures" do described_class.expects(:python).with([described_class::YUMHELPER]).returns(yumhelper_multi_arch) entries = described_class.fetch_latest_versions([], []) expect(entries.keys).to include 'nss-tools.x86_64' expect(entries.keys).to include 'pixman.x86_64' expect(entries.keys).to include 'nss-tools.i386' expect(entries.keys).to include 'pixman.i386' expect(entries['nss-tools']).to have(2).items expect(entries['pixman']).to have(2).items end it "passes the repos to enable to the helper" do described_class.expects(:python).with do |script, *args| expect(script).to eq described_class::YUMHELPER expect(args).to eq %w[-e updates -e centosplus] end.returns('') described_class.fetch_latest_versions(['updates', 'centosplus'], []) end it "passes the repos to disable to the helper" do described_class.expects(:python).with do |script, *args| expect(script).to eq described_class::YUMHELPER expect(args).to eq %w[-d updates -d centosplus] end.returns('') described_class.fetch_latest_versions([], ['updates', 'centosplus']) end it 'passes a combination of repos to the helper' do described_class.expects(:python).with do |script, *args| expect(script).to eq described_class::YUMHELPER expect(args).to eq %w[-e os -e contrib -d updates -d centosplus] end.returns('') described_class.fetch_latest_versions(['os', 'contrib'], ['updates', 'centosplus']) end end end diff --git a/spec/unit/type/package_spec.rb b/spec/unit/type/package_spec.rb index 67541863e..7f5e15847 100755 --- a/spec/unit/type/package_spec.rb +++ b/spec/unit/type/package_spec.rb @@ -1,381 +1,374 @@ #! /usr/bin/env ruby require 'spec_helper' describe Puppet::Type.type(:package) do before do Puppet::Util::Storage.stubs(:store) end it "should have a :reinstallable feature that requires the :reinstall method" do Puppet::Type.type(:package).provider_feature(:reinstallable).methods.should == [:reinstall] end it "should have an :installable feature that requires the :install method" do Puppet::Type.type(:package).provider_feature(:installable).methods.should == [:install] end it "should have an :uninstallable feature that requires the :uninstall method" do Puppet::Type.type(:package).provider_feature(:uninstallable).methods.should == [:uninstall] end it "should have an :upgradeable feature that requires :update and :latest methods" do Puppet::Type.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.type(:package).provider_feature(:purgeable).methods.should == [:purge] end it "should have a :versionable feature" do Puppet::Type.type(:package).provider_feature(:versionable).should_not be_nil end it "should have a :package_settings feature that requires :package_settings_insync?, :package_settings and :package_settings=" do Puppet::Type.type(:package).provider_feature(:package_settings).methods.should == [:package_settings_insync?, :package_settings, :package_settings=] end it "should default to being installed" do pkg = Puppet::Type.type(:package).new(:name => "yay", :provider => :apt) pkg.should(:ensure).should == :present end describe "when validating attributes" do [:name, :source, :instance, :status, :adminfile, :responsefile, :configfiles, :category, :platform, :root, :vendor, :description, :allowcdrom, :allow_virtual, :reinstall_on_refresh].each do |param| it "should have a #{param} parameter" do Puppet::Type.type(:package).attrtype(param).should == :param end end it "should have an ensure property" do Puppet::Type.type(:package).attrtype(:ensure).should == :property end it "should have a package_settings property" do Puppet::Type.type(:package).attrtype(:package_settings).should == :property end end describe "when validating attribute values" do before :each do @provider = stub( 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil, :validate_source => nil ) Puppet::Type.type(:package).defaultprovider.stubs(:new).returns(@provider) end after :each do Puppet::Type.type(:package).defaultprovider = nil end it "should support :present as a value to :ensure" do Puppet::Type.type(:package).new(:name => "yay", :ensure => :present) end it "should alias :installed to :present as a value to :ensure" do pkg = Puppet::Type.type(:package).new(:name => "yay", :ensure => :installed) pkg.should(:ensure).should == :present end it "should support :absent as a value to :ensure" do Puppet::Type.type(:package).new(: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.type(:package).new(: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) expect { Puppet::Type.type(:package).new(:name => "yay", :ensure => :purged) }.to 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.type(:package).new(: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) expect { Puppet::Type.type(:package).new(:name => "yay", :ensure => :latest) }.to 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.type(:package).new(: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) expect { Puppet::Type.type(:package).new(:name => "yay", :ensure => "1.0") }.to raise_error(Puppet::Error) end it "should accept any string as an argument to :source" do expect { Puppet::Type.type(:package).new(:name => "yay", :source => "stuff") }.to_not raise_error end it "should not accept a non-string name" do expect do Puppet::Type.type(:package).new(:name => ["error"]) end.to raise_error(Puppet::ResourceError, /Name must be a String/) end - it "should issue deprecation warning for default allow_virtual for a provider that supports virtual packages" do - Puppet.expects(:deprecation_warning).with('The package type\'s allow_virtual parameter will be changing its default value from false to true in a future release. If you do not want to allow virtual packages, please explicitly set allow_virtual to false.') - Puppet::Type.type(:package).new(:name => 'yay', :provider => :yum) - end - - it "should not issue deprecation warning for allow_virtual set to false for a provider that supports virtual packages" do - Puppet.expects(:deprecation_warning).never - Puppet::Type.type(:package).new(:name => 'yay', :provider => :yum, :allow_virtual => false) - end - - it "should not issue deprecation warning for allow_virtual set to true for a provider that supports virtual packages" do - Puppet.expects(:deprecation_warning).never - Puppet::Type.type(:package).new(:name => 'yay', :provider => :yum, :allow_virtual => true) - end - - it "should not issue deprecation warning for default allow_virtual for a provider that does not support virtual packages" do - Puppet.expects(:deprecation_warning).never - Puppet::Type.type(:package).new(:name => 'yay', :provider => :apt) - end end module PackageEvaluationTesting def setprops(properties) @provider.stubs(:properties).returns(properties) end end describe Puppet::Type.type(:package) do before :each do @provider = stub( 'provider', :class => Puppet::Type.type(:package).defaultprovider, :clear => nil, :satisfies? => true, :name => :mock, :validate_source => nil ) Puppet::Type.type(:package).defaultprovider.stubs(:new).returns(@provider) Puppet::Type.type(:package).defaultprovider.stubs(:instances).returns([]) @package = Puppet::Type.type(:package).new(:name => "yay") @catalog = Puppet::Resource::Catalog.new @catalog.add_resource(@package) end describe Puppet::Type.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).at_least_once @catalog.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) @catalog.apply end end end describe Puppet::Type.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).at_least_once @catalog.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) @catalog.apply end end end describe Puppet::Type.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).at_least_once @catalog.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) @catalog.apply end end end describe Puppet::Type.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) @catalog.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) @catalog.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 @catalog.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 @catalog.apply end end describe Puppet::Type.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) @package.property(:ensure).insync?(state).should be_false @provider.expects(:install) @catalog.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") @package.property(:ensure).insync?('1.0').should be_true @provider.expects(:install).never @catalog.apply end it "should install if the current version is not equal to the specified version" do @provider.stubs(:properties).returns(:ensure => "2.0") @package.property(:ensure).insync?('2.0').should be_false @provider.expects(:install) @catalog.apply end describe "when current value is an array" do let(:installed_versions) { ["1.0", "2.0", "3.0"] } before (:each) do @provider.stubs(:properties).returns(:ensure => installed_versions) end it "should install if value not in the array" do @package[:ensure] = "1.5" @package.property(:ensure).insync?(installed_versions).should be_false @provider.expects(:install) @catalog.apply end it "should not install if value is in the array" do @package[:ensure] = "2.0" @package.property(:ensure).insync?(installed_versions).should be_true @provider.expects(:install).never @catalog.apply end describe "when ensure is set to 'latest'" do it "should not install if the value is in the array" do @provider.expects(:latest).returns("3.0") @package[:ensure] = "latest" @package.property(:ensure).insync?(installed_versions).should be_true @provider.expects(:install).never @catalog.apply end end end end describe Puppet::Type.type(:package), "when responding to refresh" do include PackageEvaluationTesting it "should support :true as a value to :reinstall_on_refresh" do srv = Puppet::Type.type(:package).new(:name => "yay", :reinstall_on_refresh => :true) srv[:reinstall_on_refresh].should == :true end it "should support :false as a value to :reinstall_on_refresh" do srv = Puppet::Type.type(:package).new(:name => "yay", :reinstall_on_refresh => :false) srv[:reinstall_on_refresh].should == :false end it "should specify :false as the default value of :reinstall_on_refresh" do srv = Puppet::Type.type(:package).new(:name => "yay") srv[:reinstall_on_refresh].should == :false end [:latest, :present, :installed].each do |state| it "should reinstall if it should be #{state.to_s} and reinstall_on_refresh is true" do @package[:ensure] = state @package[:reinstall_on_refresh] = :true @provider.stubs(:reinstallable?).returns(true) @provider.expects(:reinstall).once @package.refresh end it "should reinstall if it should be #{state.to_s} and reinstall_on_refresh is false" do @package[:ensure] = state @package[:reinstall_on_refresh] = :false @provider.stubs(:reinstallable?).returns(true) @provider.expects(:reinstall).never @package.refresh end end [:purged, :absent, :held].each do |state| it "should not reinstall if it should be #{state.to_s} and reinstall_on_refresh is true" do @package[:ensure] = state @provider.stubs(:reinstallable?).returns(true) @provider.expects(:reinstall).never @package.refresh end it "should not reinstall if it should be #{state.to_s} and reinstall_on_refresh is false" do @package[:ensure] = state @provider.stubs(:reinstallable?).returns(true) @provider.expects(:reinstall).never @package.refresh end end end end + + describe "allow_virtual" do + it "defaults to true on platforms that support virtual packages" do + pkg = Puppet::Type.type(:package).new(:name => 'yay', :provider => :yum) + expect(pkg[:allow_virtual]).to eq true + end + + it "defaults to false on platforms that do not support virtual packages" do + pkg = Puppet::Type.type(:package).new(:name => 'yay', :provider => :apple) + expect(pkg[:allow_virtual]).to be_nil + end + end end