diff options
author | Toomas Pelberg <toomas.pelberg@playtech.com> | 2010-10-20 09:37:08 +0300 |
---|---|---|
committer | Toomas Pelberg <toomas.pelberg@playtech.com> | 2010-10-20 09:37:08 +0300 |
commit | cb401f74de55b4eba0f647667872de4cb408d525 (patch) | |
tree | cb334d97b30aa5f8464539450f21abcc75c48932 | |
parent | 04e3bf9aafa941b4e4488488b3af303c60664363 (diff) | |
download | chef-cb401f74de55b4eba0f647667872de4cb408d525.tar.gz |
Handle problem2 in http://tickets.opscode.com/browse/CHEF-1439, no need to recurse the provided by if already installed
-rw-r--r-- | chef/lib/chef/provider/package/apt.rb | 67 | ||||
-rw-r--r-- | chef/spec/unit/provider/package/apt_spec.rb | 174 |
2 files changed, 147 insertions, 94 deletions
diff --git a/chef/lib/chef/provider/package/apt.rb b/chef/lib/chef/provider/package/apt.rb index ae43e76550..14cf2bbb06 100644 --- a/chef/lib/chef/provider/package/apt.rb +++ b/chef/lib/chef/provider/package/apt.rb @@ -24,45 +24,58 @@ class Chef class Provider class Package class Apt < Chef::Provider::Package + + include Chef::Mixin::ShellOut + attr_accessor :virtual def load_current_resource @current_resource = Chef::Resource::Package.new(@new_resource.name) @current_resource.package_name(@new_resource.package_name) - - Chef::Log.debug("Checking apt-cache policy for #{@new_resource.package_name}") - status = popen4("apt-cache policy #{@new_resource.package_name}") do |pid, stdin, stdout, stderr| - stdout.each do |line| - case line - when /^\s{2}Installed: (.+)$/ - installed_version = $1 - if installed_version == '(none)' - Chef::Log.debug("Current version is nil") - @current_resource.version(nil) - else - Chef::Log.debug("Current version is #{installed_version}") - @current_resource.version(installed_version) - end - when /^\s{2}Candidate: (.+)$/ - Chef::Log.debug("Current version is #{$1}") - @candidate_version = $1 + check_package_state(@new_resource.package_name) + @current_resource + end + + def check_package_state(package) + Chef::Log.debug("Checking package status for #{package}") + installed = false + depends = false + + shell_out!("aptitude show #{package}").stdout.each_line do |line| + case line + when /^State: installed/ + installed = true + when /^Version: (.*)/ + @candidate_version = $1 + if installed + @current_resource.version($1) + else + @current_resource.version(nil) end + when /Depends: (.*)$/ + depends = $1 + when /Provided by: ([\w\d\-\.]*)/ + next if installed + virtual_provider = $1 + virtual_provider = depends if depends + Chef::Log.debug("Virtual package provided by #{virtual_provider}") + @virtual = true + installed = check_package_state(virtual_provider) + @candidate_version = virtual_provider end end - unless status.exitstatus == 0 - raise Chef::Exceptions::Package, "apt-cache failed - #{status.inspect}!" - end - - if @candidate_version == "(none)" + if @candidate_version.nil? raise Chef::Exceptions::Package, "apt does not have a version of package #{@new_resource.package_name}" end - - @current_resource + + return installed end def install_package(name, version) + package_name = "#{name}=#{version}" + package_name = "#{name} #{@candidate_version}" if @virtual run_command_with_systems_locale( - :command => "apt-get -q -y#{expand_options(@new_resource.options)} install #{name}=#{version}", + :command => "apt-get -q -y#{expand_options(@new_resource.options)} install #{package_name}", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } @@ -74,8 +87,10 @@ class Chef end def remove_package(name, version) + package_name = "#{name}" + package_name = "#{name} #{@candidate_version}" if @virtual run_command_with_systems_locale( - :command => "apt-get -q -y#{expand_options(@new_resource.options)} remove #{@new_resource.package_name}", + :command => "apt-get -q -y#{expand_options(@new_resource.options)} remove #{package_name}", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } diff --git a/chef/spec/unit/provider/package/apt_spec.rb b/chef/spec/unit/provider/package/apt_spec.rb index 55b183d1da..c47c40be87 100644 --- a/chef/spec/unit/provider/package/apt_spec.rb +++ b/chef/spec/unit/provider/package/apt_spec.rb @@ -23,93 +23,131 @@ describe Chef::Provider::Package::Apt do @node = Chef::Node.new @node.cookbook_collection = {} @run_context = Chef::RunContext.new(@node, {}) - @new_resource = Chef::Resource::Package.new("emacs", @run_context) - @current_resource = Chef::Resource::Package.new("emacs", @run_context) + @new_resource = Chef::Resource::Package.new("irssi", @run_context) + @current_resource = Chef::Resource::Package.new("irssi", @run_context) @status = mock("Status", :exitstatus => 0) @provider = Chef::Provider::Package::Apt.new(@new_resource, @run_context) Chef::Resource::Package.stub!(:new).and_return(@current_resource) @provider.stub!(:popen4).and_return(@status) @stdin = mock("STDIN", :null_object => true) - @stdout = mock("STDOUT", :null_object => true) - @stdout.stub!(:each).and_yield("emacs:"). - and_yield(" Installed: (none)"). - and_yield(" Candidate: 0.1.1"). - and_yield(" Version Table:") + @stdout =<<-PKG_STATUS +Package: irssi +State: not installed +Version: 0.8.12-7 +PKG_STATUS @stderr = mock("STDERR", :null_object => true) @pid = mock("PID", :null_object => true) + @shell_out = OpenStruct.new(:stdout => @stdout,:stdin => @stdin,:stderr => @stderr,:status => @status,:exitstatus => 0) end describe "when loading current resource" do it "should create a current resource with the name of the new_resource" do + @provider.should_receive(:shell_out!).and_return(@shell_out) Chef::Resource::Package.should_receive(:new).and_return(@current_resource) @provider.load_current_resource end it "should set the current resources package name to the new resources package name" do + @provider.should_receive(:shell_out!).and_return(@shell_out) @current_resource.should_receive(:package_name).with(@new_resource.package_name) @provider.load_current_resource end - it "should run apt-cache policy with the package name" do - @provider.should_receive(:popen4).with("apt-cache policy #{@new_resource.package_name}").and_return(@status) + it "should run aptitude show with the package name" do + @provider.should_receive(:shell_out!).with("aptitude show #{@new_resource.package_name}").and_return(@shell_out) @provider.load_current_resource end - it "should read stdout on apt-cache policy" do - @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) - @stdout.should_receive(:each).and_return(true) - @provider.load_current_resource - end - - it "should set the installed version to nil on the current resource if apt-cache policy installed version is (none)" do - @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) + it "should set the installed version to nil on the current resource if package state is not installed" do + @provider.should_receive(:shell_out!).and_return(@shell_out) @current_resource.should_receive(:version).with(nil).and_return(true) @provider.load_current_resource end - it "should set the installed version if apt-cache policy has one" do - @stdout.stub!(:each).and_yield("emacs:"). - and_yield(" Installed: 0.1.1"). - and_yield(" Candidate: 0.1.1"). - and_yield(" Version Table:") - @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) - @current_resource.should_receive(:version).with("0.1.1").and_return(true) + it "should set the installed version if package has one" do + @stdout.replace(<<-INSTALLED) +Package: sudo +State: installed +Automatically installed: no +Version: 1.7.2p1-1ubuntu5 +Priority: important +Section: admin +Maintainer: Ubuntu Core Developers <ubuntu-devel-discuss@lists.ubuntu.com> +Uncompressed Size: 602k +Depends: libc6 (>= 2.8), libpam0g (>= 0.99.7.1), libpam-modules +Conflicts: sudo-ldap +Replaces: sudo-ldap +Provided by: sudo-ldap +Description: Provide limited super user privileges to specific users +Sudo is a program designed to allow a sysadmin to give limited root privileges +to users and log root activity. The basic philosophy is to give as few +privileges as possible but still allow people to get their work done. +INSTALLED + @provider.should_receive(:shell_out!).and_return(@shell_out) @provider.load_current_resource + @current_resource.version.should == "1.7.2p1-1ubuntu5" + @provider.candidate_version.should eql("1.7.2p1-1ubuntu5") end - it "should set the candidate version if apt-cache policy has one" do - @stdout.stub!(:each).and_yield("emacs:"). - and_yield(" Installed: 0.1.1"). - and_yield(" Candidate: 10"). - and_yield(" Version Table:") - @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) - @provider.load_current_resource - @provider.candidate_version.should eql("10") - end - - it "should raise an exception if apt-cache policy fails" do - @status.should_receive(:exitstatus).and_return(1) + it "should raise an exception if aptitude show does not return a candidate version" do + @stdout.replace("E: Unable to locate package magic") + @provider.should_receive(:shell_out!).and_return(@shell_out) lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package) end - it "should not raise an exception if apt-cache policy succeeds" do - @status.should_receive(:exitstatus).and_return(0) - lambda { @provider.load_current_resource }.should_not raise_error(Chef::Exceptions::Package) + it "should return the current resouce" do + @provider.should_receive(:shell_out!).and_return(@shell_out) + @provider.load_current_resource.should eql(@current_resource) end - it "should raise an exception if apt-cache policy does not return a candidate version" do - @stdout.stub!(:each).and_yield("emacs:"). - and_yield(" Installed: 0.1.1"). - and_yield(" Candidate: (none)"). - and_yield(" Version Table:") - @provider.stub!(:popen4).and_yield(@pid, @stdin, @stdout, @stderr).and_return(@status) - lambda { @provider.load_current_resource }.should raise_error(Chef::Exceptions::Package) + it "should set candidate version to new package name if virtual package" do + @new_resource.package_name("libmysqlclient-dev") + virtual_package_out=<<-VPKG_STDOUT +"No current or candidate version found for libmysqlclient-dev"). +Package: libmysqlclient-dev +State: not a real package +Provided by: libmysqlclient15-dev +VPKG_STDOUT + virtual_package = mock(:stdout => virtual_package_out,:exitstatus => 0) + @provider.should_receive(:shell_out!).with("aptitude show libmysqlclient-dev").and_return(virtual_package) + real_package_out=mock("STDOUT", :null_object => true) + real_package_out =<<-REALPKG_STDOUT +Package: libmysqlclient15-dev +State: not installed +Version: 5.0.51a-24+lenny4 +REALPKG_STDOUT + real_package = mock(:stdout => real_package_out,:exitstatus => 0) + @provider.should_receive(:shell_out!).with("aptitude show libmysqlclient15-dev").and_return(real_package) + @provider.load_current_resource + @provider.candidate_version.should eql("libmysqlclient15-dev") end - it "should return the current resouce" do - @provider.load_current_resource.should eql(@current_resource) + it "should set candidate version to the depends package name if multiple virtual package providers" do + @new_resource.package_name("mysql-client") + virtual_package_out=<<-VPKG_STDOUT +Package: mysql-client +State: not installed +Version: 5.1.41-3ubuntu12.6 +Depends: mysql-client-5.1 +Provided by: mysql-cluster-client-5.1, mysql-client-5.1 +Description: MySQL database client (metapackage depending on the latest version) +VPKG_STDOUT + virtual_package = mock(:stdout => virtual_package_out,:exitstatus => 0) + @provider.should_receive(:shell_out!).with("aptitude show mysql-client").and_return(virtual_package) + real_package_out=<<-REALPKG_STDOUT +Package: mysql-client-5.1 +State: not installed +Version: Version: 5.1.41-3ubuntu12.6 +Conflicts: mysql-client (< 5.1.41-3ubuntu12.6), mysql-client-5.0 +Replaces: mysql-client (< 5.1.41-3ubuntu12.6), mysql-client-5.0 +Provides: mysql-client, mysql-client-4.1, virtual-mysql-client +REALPKG_STDOUT + real_package = mock(:stdout => real_package_out,:exitstatus => 0) + @provider.should_receive(:shell_out!).with("aptitude show mysql-client-5.1").and_return(real_package) + @provider.load_current_resource + @provider.candidate_version.should eql("mysql-client-5.1") end end @@ -118,32 +156,32 @@ describe Chef::Provider::Package::Apt do it "should run apt-get install with the package name and version" do @provider.should_receive(:run_command_with_systems_locale).with({ - :command => "apt-get -q -y install emacs=1.0", + :command => "apt-get -q -y install irssi=0.8.12-7", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } }) - @provider.install_package("emacs", "1.0") + @provider.install_package("irssi", "0.8.12-7") end it "should run apt-get install with the package name and version and options if specified" do @provider.should_receive(:run_command_with_systems_locale).with({ - :command => "apt-get -q -y --force-yes install emacs=1.0", + :command => "apt-get -q -y --force-yes install irssi=0.8.12-7", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } }) @new_resource.stub!(:options).and_return("--force-yes") - @provider.install_package("emacs", "1.0") + @provider.install_package("irssi", "0.8.12-7") end end describe Chef::Provider::Package::Apt, "upgrade_package" do it "should run install_package with the name and version" do - @provider.should_receive(:install_package).with("emacs", "1.0") - @provider.upgrade_package("emacs", "1.0") + @provider.should_receive(:install_package).with("irssi", "0.8.12-7") + @provider.upgrade_package("irssi", "0.8.12-7") end end @@ -151,24 +189,24 @@ describe Chef::Provider::Package::Apt do it "should run apt-get remove with the package name" do @provider.should_receive(:run_command_with_systems_locale).with({ - :command => "apt-get -q -y remove emacs", + :command => "apt-get -q -y remove irssi", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } }) - @provider.remove_package("emacs", "1.0") + @provider.remove_package("irssi", "0.8.12-7") end it "should run apt-get remove with the package name and options if specified" do @provider.should_receive(:run_command_with_systems_locale).with({ - :command => "apt-get -q -y --force-yes remove emacs", + :command => "apt-get -q -y --force-yes remove irssi", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } }) @new_resource.stub!(:options).and_return("--force-yes") - @provider.remove_package("emacs", "1.0") + @provider.remove_package("irssi", "0.8.12-7") end end @@ -176,52 +214,52 @@ describe Chef::Provider::Package::Apt do it "should run apt-get purge with the package name" do @provider.should_receive(:run_command_with_systems_locale).with({ - :command => "apt-get -q -y purge emacs", + :command => "apt-get -q -y purge irssi", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } }) - @provider.purge_package("emacs", "1.0") + @provider.purge_package("irssi", "0.8.12-7") end it "should run apt-get purge with the package name and options if specified" do @provider.should_receive(:run_command_with_systems_locale).with({ - :command => "apt-get -q -y --force-yes purge emacs", + :command => "apt-get -q -y --force-yes purge irssi", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } }) @new_resource.stub!(:options).and_return("--force-yes") - @provider.purge_package("emacs", "1.0") + @provider.purge_package("irssi", "0.8.12-7") end end describe "when preseeding a package" do before(:each) do - @provider.stub!(:get_preseed_file).and_return("/tmp/emacs-10.seed") + @provider.stub!(:get_preseed_file).and_return("/tmp/irssi-0.8.12-7.seed") @provider.stub!(:run_command_with_systems_locale).and_return(true) end it "should get the full path to the preseed response file" do - @provider.should_receive(:get_preseed_file).with("emacs", "10").and_return("/tmp/emacs-10.seed") - @provider.preseed_package("emacs", "10") + @provider.should_receive(:get_preseed_file).with("irssi", "0.8.12-7").and_return("/tmp/irssi-0.8.12-7.seed") + @provider.preseed_package("irssi", "0.8.12-7") end it "should run debconf-set-selections on the preseed file if it has changed" do @provider.should_receive(:run_command_with_systems_locale).with({ - :command => "debconf-set-selections /tmp/emacs-10.seed", + :command => "debconf-set-selections /tmp/irssi-0.8.12-7.seed", :environment => { "DEBIAN_FRONTEND" => "noninteractive" } }).and_return(true) - @provider.preseed_package("emacs", "10") + @provider.preseed_package("irssi", "0.8.12-7") end it "should not run debconf-set-selections if the preseed file has not changed" do @provider.stub!(:get_preseed_file).and_return(false) @provider.should_not_receive(:run_command_with_systems_locale) - @provider.preseed_package("emacs", "10") + @provider.preseed_package("irssi", "0.8.12-7") end end end |