diff --git a/lib/puppet/provider/package/pkgutil.rb b/lib/puppet/provider/package/pkgutil.rb index d6c0d3361..a0f39a09e 100755 --- a/lib/puppet/provider/package/pkgutil.rb +++ b/lib/puppet/provider/package/pkgutil.rb @@ -1,183 +1,174 @@ # Packaging using Peter Bonivart's pkgutil program. Puppet::Type.type(:package).provide :pkgutil, :parent => :sun, :source => :sun do desc "Package management using Peter Bonivart's ``pkgutil`` command on Solaris." pkgutil_bin = "pkgutil" if FileTest.executable?("/opt/csw/bin/pkgutil") pkgutil_bin = "/opt/csw/bin/pkgutil" end confine :operatingsystem => :solaris commands :pkguti => pkgutil_bin def self.healthcheck() unless FileTest.exists?("/var/opt/csw/pkgutil/admin") Puppet.notice "It is highly recommended you create '/var/opt/csw/pkgutil/admin'." Puppet.notice "See /var/opt/csw/pkgutil" end correct_wgetopts = false [ "/opt/csw/etc/pkgutil.conf", "/etc/opt/csw/pkgutil.conf" ].each do |confpath| File.open(confpath) do |conf| conf.each {|line| correct_wgetopts = true if line =~ /^\s*wgetopts\s*=.*(-nv|-q|--no-verbose|--quiet)/ } end end if ! correct_wgetopts Puppet.notice "It is highly recommended that you set 'wgetopts=-nv' in your pkgutil.conf." end end def self.instances(hash = {}) healthcheck # Use the available pkg list (-a) to work out aliases aliases = {} availlist.each do |pkg| aliases[pkg[:name]] = pkg[:alias] end # The -c pkglist lists installed packages pkginsts = [] - output = pkguti(["-c"]) - parse_pkglist(output).each do |pkg| + pkglist(hash).each do |pkg| pkg.delete(:avail) pkginsts << new(pkg) # Create a second instance with the alias if it's different pkgalias = aliases[pkg[:name]] if pkgalias and pkg[:name] != pkgalias apkg = pkg.dup apkg[:name] = pkgalias pkginsts << new(apkg) end end pkginsts end # Turns a pkgutil -a listing into hashes with the common alias, full # package name and available version def self.availlist output = pkguti ["-a"] list = output.split("\n").collect do |line| next if line =~ /^common\s+package/ # header of package list next if noise?(line) if line =~ /\s*(\S+)\s+(\S+)\s+(.*)/ { :alias => $1, :name => $2, :avail => $3 } else Puppet.warning "Cannot match %s" % line end end.reject { |h| h.nil? } end - # Turn our pkgutil -c listing into a hash for a single package. - def pkgsingle(resource) - # The --single option speeds up the execution, because it queries - # the package managament system for one package only. - command = ["-c", "--single", resource[:name]] - self.class.parse_pkglist(run_pkgutil(resource, command), { :justme => resource[:name] }) - end - # Turn our pkgutil -c listing into a bunch of hashes. - def self.parse_pkglist(output, hash = {}) - output = output.split("\n") + # Supports :justme => packagename, which uses the optimised --single arg + def self.pkglist(hash) + command = ["-c"] + + if hash[:justme] + # The --single option speeds up the execution, because it queries + # the package managament system for one package only. + command << "--single" + command << hash[:justme] + end + + output = pkguti(command).split("\n") if output[-1] == "Not in catalog" Puppet.warning "Package not in pkgutil catalog: %s" % hash[:justme] return nil end list = output.collect do |line| next if line =~ /installed\s+catalog/ # header of package list next if noise?(line) pkgsplit(line) end.reject { |h| h.nil? } if hash[:justme] # Single queries may have been for an alias so return the name requested if list.any? list[-1][:name] = hash[:justme] return list[-1] end else list.reject! { |h| h[:ensure] == :absent } return list end end # Identify common types of pkgutil noise as it downloads catalogs etc def self.noise?(line) true if line =~ /^#/ true if line =~ /^Checking integrity / # use_gpg true if line =~ /^gpg: / # gpg verification true if line =~ /^=+> / # catalog fetch true if line =~ /\d+:\d+:\d+ URL:/ # wget without -q false end # Split the different lines into hashes. def self.pkgsplit(line) if line =~ /\s*(\S+)\s+(\S+)\s+(.*)/ hash = {} hash[:name] = $1 hash[:ensure] = if $2 == "notinst" :absent else $2 end hash[:avail] = $3 if hash[:avail] =~ /^SAME\s*$/ hash[:avail] = hash[:ensure] end # Use the name method, so it works with subclasses. hash[:provider] = self.name return hash else Puppet.warning "Cannot match %s" % line return nil end end - def run_pkgutil(resource, *args) - # Allow source to be one or more URLs pointing to a repository that all - # get passed to pkgutil via one or more -t options - if resource[:source] - pkguti *[resource[:source].map{|src| [ "-t", src ]}, *args].flatten - else - pkguti *args.flatten - end - end - def install - run_pkgutil @resource, "-y", "-i", @resource[:name] + pkguti "-y", "-i", @resource[:name] end # Retrieve the version from the current package file. def latest - hash = pkgsingle(@resource) + hash = self.class.pkglist(:justme => @resource[:name]) hash[:avail] if hash end def query - if hash = pkgsingle(@resource) + if hash = self.class.pkglist(:justme => @resource[:name]) hash else {:ensure => :absent} end end def update - run_pkgutil @resource, "-y", "-u", @resource[:name] + pkguti "-y", "-u", @resource[:name] end def uninstall - run_pkgutil @resource, "-y", "-r", @resource[:name] + pkguti "-y", "-r", @resource[:name] end end diff --git a/spec/unit/provider/package/pkgutil_spec.rb b/spec/unit/provider/package/pkgutil_spec.rb index e51add8ce..dcae21250 100755 --- a/spec/unit/provider/package/pkgutil_spec.rb +++ b/spec/unit/provider/package/pkgutil_spec.rb @@ -1,235 +1,181 @@ #!/usr/bin/env rspec require 'spec_helper' provider = Puppet::Type.type(:package).provider(:pkgutil) describe provider do before(:each) do @resource = Puppet::Type.type(:package).new( :name => "TESTpkg", :ensure => :present, :provider => :pkgutil ) @provider = provider.new(@resource) # Stub all file and config tests provider.stubs(:healthcheck) end it "should have an install method" do @provider.should respond_to(:install) end it "should have a latest method" do @provider.should respond_to(:uninstall) end it "should have an update method" do @provider.should respond_to(:update) end it "should have a latest method" do @provider.should respond_to(:latest) end describe "when installing" do it "should use a command without versioned package" do @resource[:ensure] = :latest @provider.expects(:pkguti).with('-y', '-i', 'TESTpkg') @provider.install end - - it "should support a single temp repo URL" do - @resource[:ensure] = :latest - @resource[:source] = "http://example.net/repo" - @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-y', '-i', 'TESTpkg') - @provider.install - end - - it "should support multiple temp repo URLs" do - @resource[:ensure] = :latest - @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ] - @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-i', 'TESTpkg') - @provider.install - end end describe "when updating" do it "should use a command without versioned package" do @provider.expects(:pkguti).with('-y', '-u', 'TESTpkg') @provider.update end - - it "should support a single temp repo URL" do - @resource[:source] = "http://example.net/repo" - @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-y', '-u', 'TESTpkg') - @provider.update - end - - it "should support multiple temp repo URLs" do - @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ] - @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-u', 'TESTpkg') - @provider.update - end end describe "when uninstalling" do it "should call the remove operation" do @provider.expects(:pkguti).with('-y', '-r', 'TESTpkg') @provider.uninstall end - - it "should support a single temp repo URL" do - @resource[:source] = "http://example.net/repo" - @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-y', '-r', 'TESTpkg') - @provider.uninstall - end - - it "should support multiple temp repo URLs" do - @resource[:source] = [ 'http://example.net/repo', 'http://example.net/foo' ] - @provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-t', 'http://example.net/foo', '-y', '-r', 'TESTpkg') - @provider.uninstall - end end describe "when getting latest version" do it "should return TESTpkg's version string" do fake_data = " noisy output here TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data - @provider.latest.should == "1.4.5,REV=2007.11.20" - end - - it "should support a temp repo URL" do - @resource[:source] = "http://example.net/repo" - fake_data = " -noisy output here -TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" - provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-c', '--single', 'TESTpkg').returns fake_data + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data @provider.latest.should == "1.4.5,REV=2007.11.20" end it "should handle TESTpkg's 'SAME' version string" do fake_data = " noisy output here TESTpkg 1.4.5,REV=2007.11.18 SAME" - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data @provider.latest.should == "1.4.5,REV=2007.11.18" end it "should handle a non-existent package" do fake_data = "noisy output here Not in catalog" - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data @provider.latest.should == nil end it "should warn on unknown pkgutil noise" do - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns("testingnoise") + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns("testingnoise") @provider.latest.should == nil end it "should ignore pkgutil noise/headers to find TESTpkg" do fake_data = "# stuff => Fetching new catalog and descriptions (http://mirror.opencsw.org/opencsw/unstable/i386/5.11) if available ... 2011-02-19 23:05:46 URL:http://mirror.opencsw.org/opencsw/unstable/i386/5.11/catalog [534635/534635] -> \"/var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11.tmp\" [1] Checking integrity of /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11 with gpg. gpg: Signature made February 17, 2011 05:27:53 PM GMT using DSA key ID E12E9D2F gpg: Good signature from \"Distribution Manager \" ==> 2770 packages loaded from /var/opt/csw/pkgutil/catalog.mirror.opencsw.org_opencsw_unstable_i386_5.11 package installed catalog TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data @provider.latest.should == "1.4.5,REV=2007.11.20" end it "should find REALpkg via an alias (TESTpkg)" do fake_data = " noisy output here REALpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data @provider.query[:name].should == "TESTpkg" end end describe "when querying current version" do it "should return TESTpkg's version string" do fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data @provider.query[:ensure].should == "1.4.5,REV=2007.11.18" end it "should handle a package that isn't installed" do fake_data = "TESTpkg notinst 1.4.5,REV=2007.11.20" - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data @provider.query[:ensure].should == :absent end it "should handle a non-existent package" do fake_data = "noisy output here Not in catalog" - provider.expects(:pkguti).with('-c', '--single', 'TESTpkg').returns fake_data + provider.expects(:pkguti).with(['-c', '--single', 'TESTpkg']).returns fake_data @provider.query[:ensure].should == :absent end - - it "should support a temp repo URL" do - @resource[:source] = "http://example.net/repo" - fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" - provider.expects(:pkguti).with('-t', 'http://example.net/repo', '-c', '--single', 'TESTpkg').returns fake_data - @provider.query[:ensure].should == "1.4.5,REV=2007.11.18" - end end describe "when querying current instances" do it "should warn on unknown pkgutil noise" do provider.expects(:pkguti).with(['-a']).returns("testingnoise") provider.expects(:pkguti).with(['-c']).returns("testingnoise") Puppet.expects(:warning).times(2) provider.expects(:new).never provider.instances.should == [] end it "should return TESTpkg's version string" do fake_data = "TESTpkg TESTpkg 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-a']).returns fake_data fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-c']).returns fake_data testpkg = mock 'pkg1' provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg provider.instances.should == [testpkg] end it "should also return both TESTpkg and mypkg alias instances" do fake_data = "mypkg TESTpkg 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-a']).returns fake_data fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-c']).returns fake_data testpkg = mock 'pkg1' provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg aliaspkg = mock 'pkg2' provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "mypkg", :provider => :pkgutil).returns aliaspkg provider.instances.should == [testpkg,aliaspkg] end it "shouldn't mind noise in the -a output" do fake_data = "noisy output here" provider.expects(:pkguti).with(['-a']).returns fake_data fake_data = "TESTpkg 1.4.5,REV=2007.11.18 1.4.5,REV=2007.11.20" provider.expects(:pkguti).with(['-c']).returns fake_data testpkg = mock 'pkg1' provider.expects(:new).with(:ensure => "1.4.5,REV=2007.11.18", :name => "TESTpkg", :provider => :pkgutil).returns testpkg provider.instances.should == [testpkg] end end end