21815619 puppet fails to install latest package versions
authorSungmin Lee <sungmin.lee@oracle.com>
Thu, 14 Apr 2016 14:24:06 -0700
changeset 5775 ecbec224a370
parent 5774 5867c90db266
child 5776 f6fa335ae47b
21815619 puppet fails to install latest package versions 21156357 support install options with package resource type
components/ruby/puppet/patches/puppet-12-pkg-provider-latest.patch
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/ruby/puppet/patches/puppet-12-pkg-provider-latest.patch	Thu Apr 14 14:24:06 2016 -0700
@@ -0,0 +1,241 @@
+In-house patch to fix Solaris specific bug; not suitible for upstream
+Fixing issue with package ensure=>latest by refreshing the pkg cache first with pkg update
+Using --parsable=0 option to obsolete pkg list -Hn
+Fixing problem with ambiguous package names
+Wildcards support
+install_options/uninstall_options features support
+
+--- puppet-3.8.6/lib/puppet/provider/package/pkg.orig	2016-04-13 14:39:48.421008932 -0700
++++ puppet-3.8.6/lib/puppet/provider/package/pkg.rb	2016-04-13 14:27:31.382336591 -0700
+@@ -1,7 +1,8 @@
++require 'json'
+ require 'puppet/provider/package'
+ 
+ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package do
+-  desc "OpenSolaris image packaging system. See pkg(5) for more information"
++  desc "Solaris image packaging system. See pkg(5) for more information"
+   # http://docs.oracle.com/cd/E19963-01/html/820-6572/managepkgs.html
+   # A few notes before we start :
+   # Opensolaris pkg has two slightly different formats (as of now.)
+@@ -14,10 +15,10 @@
+   # TODO: We still have to allow packages to specify a preferred publisher.
+ 
+   has_feature :versionable
+-
+   has_feature :upgradable
+-
+   has_feature :holdable
++  has_feature :install_options
++  has_feature :uninstall_options
+ 
+   commands :pkg => "/usr/bin/pkg"
+ 
+@@ -26,7 +27,7 @@
+   defaultfor :osfamily => :solaris, :kernelrelease => ['5.11', '5.12']
+ 
+   def self.instances
+-    pkg(:list, '-H').split("\n").map{|l| new(parse_line(l))}
++    pkg(:list, '-Hv').split("\n").map{|l| new(parse_line(l))}
+   end
+ 
+   # The IFO flag field is just what it names, the first field can have ether
+@@ -69,6 +70,10 @@
+     {}
+   end
+ 
++  def self.ufxi_flag(flags)
++    {}
++  end
++
+   # pkg state was present in the older version of pkg (with UFOXI) but is
+   # no longer available with the IFO field version. When it was present,
+   # it was used to indicate that a particular version was present (installed)
+@@ -90,29 +95,73 @@
+   # formats of output for different pkg versions.
+   def self.parse_line(line)
+     (case line.chomp
+-    # NAME (PUBLISHER)            VERSION           IFO  (new:630e1ffc7a19)
+-    # system/core-os              0.5.11-0.169      i--
+-    when /^(\S+) +(\S+) +(...)$/
+-      {:name => $1, :ensure => $2}.merge ifo_flag($3)
+-
+-    # x11/wm/fvwm (fvwm.org)      2.6.1-3           i--
+-    when /^(\S+) \((.+)\) +(\S+) +(...)$/
+-      {:name => $1, :publisher => $2, :ensure => $3}.merge ifo_flag($4)
+-
+-    # NAME (PUBLISHER)                  VERSION          STATE      UFOXI (dvd:052adf36c3f4)
+-    # SUNWcs                            0.5.11-0.126     installed  -----
+-    when /^(\S+) +(\S+) +(\S+) +(.....)$/
+-      {:name => $1, :ensure => $2}.merge pkg_state($3).merge(ufoxi_flag($4))
+-
+-    # web/firefox/plugin/flash (extra)  10.0.32.18-0.111 installed  -----
+-    when /^(\S+) \((.+)\) +(\S+) +(\S+) +(.....)$/
+-      {:name => $1, :publisher => $2, :ensure => $3}.merge pkg_state($4).merge(ufoxi_flag($5))
+-
++    #pkg list -vH output format since build 165
++    #FMRI                                                                         IFO
++    #pkg://solaris/system/[email protected]:20151116T010109Z           i--
++    when /^(\S+) +(...)$/
++      full_fmri = $1.split('@')
++      {:name => full_fmri[0], :ensure => full_fmri[1]}.merge(ifo_flag($2))
++
++    #Solaris11 Express 10/11, OpenSolaris
++    #FMRI                                                               STATE       UFOXI
++    #pkg://solaris/editor/[email protected],5.11-0.151.0.1:20101105T0540492   installed   -----
++    when /^(\S+) +(\S+) +(.....)$/
++      full_fmri = $1.split('@')
++      {:name => full_fmri[0], :ensure => full_fmri[1]}.merge pkg_state($2).merge(ufoxi_flag($3))
++
++    #OpenSolaris
++    #FMRI                                               STATE       UFXI
++    #pkg:/[email protected],5.11-0.86:20080426T180612Z    installed   ----
++    when /^(\S+) +(\S+) +(....)$/
++      full_fmri = $1.split('@')
++      {:name => full_fmri[0], :ensure => full_fmri[1]}.merge pkg_state($2).merge(ufxi_flag($3))
+     else
+       raise ArgumentError, 'Unknown line format %s: %s' % [self.name, line]
+     end).merge({:provider => self.name})
+   end
+ 
++  def parse_latest_query(exit_code, pkg_fmri_list)
++    # Get the current status of the package
++    name = @property_hash[:name]
++    provider = @property_hash[:provider]
++
++    # The package is up-to-date
++    if pkg_fmri_list.empty? && exit_code == 4
++      status = 'installed'
++      version = @property_hash[:ensure]
++    # Latest version is available
++    elsif !pkg_fmri_list.empty? && exit_code == 0
++      if wildcard?
++        version = ''
++        status = 'known'
++      else
++        # Search for the correct pkg FMRI (to ignore dependencies)
++        # search string format will be, i.e.,
++        # pkg://test/testing/testpkg@
++        # and retrieve the matched index.
++        search_pkg_name = name + '@'
++        index = pkg_fmri_list.index{|l| l[0].include?(search_pkg_name)}
++        raise Puppet::Error, "Cannot retrieve pkg name" if index.nil?
++
++        # Parse the fmri of the latest available pkg, i.e.,
++        # from: pkg://test/testing/[email protected],5.11:20151112T015434Z
++        # to: 1.0.13,5.11:20151112T015434Z
++        version = pkg_fmri_list[index][1].split('@')[1]
++        status = 'known'
++      end
++    else
++      raise Puppet::Error, "Cannot retrieve pkg version"
++    end
++
++    # Store as a desirable query
++    return {
++      :name=>name,
++      :ensure=>version,
++      :status=>status,
++      :provider=>provider
++    }
++  end
++
+   def hold
+     pkg(:freeze, @resource[:name])
+   end
+@@ -122,31 +171,22 @@
+     raise Puppet::Error, "Unable to unfreeze #{r[:out]}" unless [0,4].include? r[:exit]
+   end
+ 
+-  # Return the version of the package. Note that the bug
+-  # http://defect.opensolaris.org/bz/show_bug.cgi?id=19159%
+-  # notes that we can't use -Ha for the same even though the manual page reads that way.
++  # Return the version of the package.
+   def latest
+-    lines = pkg(:list, "-Hn", @resource[:name]).split("\n")
+-
+-    # remove certificate expiration warnings from the output, but report them
+-    # Note: we'd like to use select! here to modify the lines array and avoid
+-    #       the second select further down. But Solaris 11 comes with ruby 1.8.7
+-    #       which doesn't support select!, so do this as two selects.
+-    cert_warnings = lines.select { |line| line =~ /^Certificate/ }
+-    if cert_warnings
+-      Puppet.warning("pkg warning: #{cert_warnings}")
+-    end
+-
+-    lst = lines.select { |line| line !~ /^Certificate/ }.map { |line| self.class.parse_line(line) }
+-
+-    # Now we know there is a newer version. But is that installable? (i.e are there any constraints?)
+-    # return the first known we find. The only way that is currently available is to do a dry run of
+-    # pkg update and see if could get installed (`pkg update -n res`).
+-    known = lst.find {|p| p[:status] == 'known' }
+-    return known[:ensure] if known and exec_cmd(command(:pkg), 'update', '-n', @resource[:name])[:exit].zero?
+-
+-    # If not, then return the installed, else nil
+-    (lst.find {|p| p[:status] == 'installed' } || {})[:ensure]
++    # Dry-run package update to check the availability of the latest version
++    # -- return code --
++    # 0: latest version available
++    # 1: error
++    # 4: already up-to-date
++    r = exec_cmd(command(:pkg), 'update', '-n', '--parsable=0', @resource[:name])
++    raise Puppet::Error, "Cannot update to the latest package: #{r[:out]}\n" \
++      unless [0, 4].include? r[:exit]
++
++    package_versions = JSON.parse(r[:out])['change-packages']
++
++    lst =  parse_latest_query(r[:exit], package_versions)
++    Puppet.debug "Desirable query status = #{lst}"
++    return lst[:ensure]
+   end
+ 
+   # install the package and accept all licenses.
+@@ -162,19 +202,22 @@
+         self.uninstall if Puppet::Util::Package.versioncmp(should, is[:ensure]) < 0
+       end
+     end
+-    r = exec_cmd(command(:pkg), 'install', '--accept', name)
++
++    cmd = ['--accept']
++    cmd << join_options(@resource[:install_options]) if @resource[:install_options]
++    r = exec_cmd(command(:pkg), 'install', cmd, name)
+     return r if nofail
+     raise Puppet::Error, "Unable to update #{r[:out]}" if r[:exit] != 0
+   end
+ 
+-  # uninstall the package. The complication comes from the -r_ecursive flag which is no longer
+-  # present in newer package version.
++  # uninstall the package.
+   def uninstall
+     cmd = [:uninstall]
+     case (pkg :version).chomp
+     when /052adf36c3f4/
+       cmd << '-r'
+     end
++    cmd << join_options(@resource[:uninstall_options]) if @resource[:uninstall_options]
+     cmd << @resource[:name]
+     pkg cmd
+   end
+@@ -187,10 +230,22 @@
+     raise Puppet::Error, "Unable to update #{r[:out]}"
+   end
+ 
++  def wildcard?
++    @resource[:name].include?("*") || @resource[:name].include?("?")
++  end
++
+   # list a specific package
+   def query
+-    r = exec_cmd(command(:pkg), 'list', '-H', @resource[:name])
++    r = exec_cmd(command(:pkg), 'list', '-Hv', @resource[:name])
+     return {:ensure => :absent, :name => @resource[:name]} if r[:exit] != 0
++
++    # Does input contain wildcard? just pass it down and let pkg decide
++    return {:ensure => :installed, :name => @resource[:name], :provider => self.class.name} if wildcard?
++    # If there are more than one results? this will fail due to the ambiguity.
++    if r[:out].split("\n").length > 1
++      raise Puppet::Error, "'#{@resource[:name]}' matches multiple packages. \n#{r[:out]}"
++    end
++    # Pass the parsed result back to @property_hash
+     self.class.parse_line(r[:out])
+   end
+