components/ruby/puppet/patches/puppet-12-pkg-provider-latest.patch
author Shawn Ferry <shawn.ferry@oracle.com>
Mon, 28 Nov 2016 16:54:02 -0800
changeset 7565 48aa82a0931f
parent 5860 afd31ba91ee9
permissions -rw-r--r--
PSARC/2016/607 Puppet 4.7.0, Hiera 3.2.0 19429313 address_object type should support vrrp addresses 19888183 publisher provider is applied on each puppet run 22125767 nsswitch provider missing ipnodes, protocols, printers 22126108 add process scheduler administration provider 22960016 Puppet needs a native way to set ZFS ACLs 23107546 race condition with smf provider and manifest-import 23119445 Upgrade Ruby Hiera to 3.2.0 23547788 Add ILB type to Puppet 23593229 rspec tests need to be written for solaris_providers boot_environment 23593308 rspec tests need to be written for solaris_providers ipmp_interface 23593316 rspec tests need to be written for solaris_providers protocol_properties 23593225 rspec tests need to be written for solaris_providers etherstub 23593319 rspec tests need to be written for solaris_providers vnic 23593310 puppet module rspec tests and validation for nsswitch 24674283 current puppet.stencil implementation restricts some valid puppet use cases 24680838 puppet agent failures should be visible to administrator via SMF interfaces 24681179 puppet's logadm.conf is pointing at wrong location for master, ignoring agent 24696742 puppet svccfg doesn't work for some value types 24696809 Puppet link aggregation modules cascading errors 24825868 Update to Puppet 4, Puppet 3 is EOL 24836004 '-' is valid in pkg mediator implementation 24836209 nis provider needs to support multiple securenets entries 24928890 keystone and horizon modules patches create invalid metadata.json 25022632 puppet ipmp_interface type should not validate interface existence 25022714 Puppet SMF service should not refresh on every apply operation 25225039 puppet svccfg should not declare a property absent if it does not match desired 25060925 puppet resource address_object generates invalid puppet code for dhcp interfaces 25065015 update puppet oracle-solaris_providers 2.0.0 25071521 puppet svccfg type should return a clear error if value is not provided 25071681 puppet dns resource generates invalid manifest 25071686 puppet resource ldap; Error: Could not run: No ability to determine if ldap... 25071690 puppet resource nis; Error: Could not run: No ability to determine if nis exists 25092384 puppet module files should be owned by root 25093408 puppet-solaris should be part of puppet again 25106150 Nis provider is not idempotent 25106155 DNS provider is not idempotent 25163776 puppet link_aggregation misunderstands 'address' -u output 25163791 puppet link_aggregation should use resource values instead of property_hash 25163815 puppet address_object errors and validations could be better 25163840 puppet Puppet::Property::List types conflict with internally generated arrays 25163864 puppet link_aggregation type specs need to be written 25177901 puppet beadm should not use both -e and -p 25178928 puppet link_aggregation should try to copy existing values on change of mode 25179040 puppet link_aggregation should delete with -t for temporary 25192742 puppet svccfg shouldn't try to update properties for a non-existent fmri 25196056 puppet interface and address _properties namevars are problematic 25191982 puppet type 'dns' is not able to set 'options' property in resolv.conf 25211935 puppet link_aggregation needs to permanently delete before modifying temporary 25217063 puppet protocol_properties is not idempotent 25218036 puppet resource svccfg emits a warning for every property 25218053 puppet svccfg prefetch should match individually specified parameters 25218208 puppet svccfg should enforce well-formedness in fmri parameters 25224661 puppet resource address_properties shouldn't output read-only properties 25224777 puppet address_properties should not reset unchanged properties 25306835 puppet boot_environment needs to understand the new snapshot format 25306877 puppet svccfg should check for pg and allow nested property groups 25306904 puppet dns,nis,ldap,protocol_properties prefetch fails after input auto munge 25348321 puppet boot_environment needs to validate all properties and parameters 25354751 puppet vnic provider needs to support / and - as valid vnic name characters

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

diff --git a/lib/puppet/provider/package/pkg.rb b/lib/puppet/provider/package/pkg.rb
--- a/lib/puppet/provider/package/pkg.rb
+++ b/lib/puppet/provider/package/pkg.rb
@@ -1,7 +1,7 @@
 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"
   # https://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 +14,10 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
   # 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"
 
@@ -69,6 +69,10 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
     {}
   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)
@@ -100,11 +104,74 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
     when %r'^pkg://([^/]+)/([^@]+)@(\S+) +(\S+) +(.....)$'
       {:publisher => $1, :name => $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
@@ -167,28 +234,28 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
     false
   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.
   def latest
-    lines = pkg(:list, "-Hvn", @resource[:name]).split("\n")
+    # 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]).split('\n')
+    package_versions = JSON.parse(r[:out])['change-packages']
 
     # remove certificate expiration warnings from the output, but report them
-    cert_warnings = lines.select { |line| line =~ /^Certificate/ }
+    cert_warnings = r[:out].select { |line| line =~ /^Certificate/ }
     unless cert_warnings.empty?
       Puppet.warning("pkg warning: #{cert_warnings.join(', ')}")
     end
+    lst = r[:out].select { |line| line !~ /^Certificate/ }.map { |line| self.class.parse_line(line) }
 
-    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?
+    raise Puppet::Error, "Cannot update to the latest package: #{r[:out]}\n" \
+      unless [0, 4].include? r[:exit]
 
-    # If not, then return the installed, else nil
-    (lst.find {|p| p[:status] == 'installed' } || {})[:ensure]
+    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.
@@ -206,7 +273,10 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
     unless should.is_a? Symbol
       name += "@#{should}"
     end
-    r = exec_cmd(command(:pkg), command, '--accept', name)
+
+    opts = ['--accept']
+    opts << join_options(@resource[:install_options]) if @resource[:install_options]
+    r = exec_cmd(command(:pkg), 'install', opts, name)
     return r if nofail
     raise Puppet::Error, "Unable to update #{r[:out]}" if r[:exit] != 0
   end
@@ -219,6 +289,7 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
     when /052adf36c3f4/
       cmd << '-r'
     end
+    cmd << join_options(@resource[:uninstall_options]) if @resource[:uninstall_options]
     cmd << @resource[:name]
     pkg cmd
   end
@@ -231,10 +302,22 @@ Puppet::Type.type(:package).provide :pkg, :parent => Puppet::Provider::Package d
     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', '-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