$OpenBSD: patch-lib_puppet_provider_package_openbsd_rb,v 1.29 2015/08/24 11:24:26 jasper Exp $

From a85e6635d608a280a1766d21da8d5d114b04034e Mon Sep 17 00:00:00 2001
From: Jasper Lievisse Adriaanse <jasper@humppa.nl>
Date: Wed, 29 Oct 2014 16:23:39 +0100
Subject: [PATCH] (PUP-3604) Rework handling of :ensure with a version

Handle the flavor as a property, not as a parameter.

Handle errors from pkg_add

--- lib/puppet/provider/package/openbsd.rb.orig	Thu Aug  6 18:17:42 2015
+++ lib/puppet/provider/package/openbsd.rb	Fri Aug 21 15:03:15 2015
@@ -20,48 +20,49 @@ Puppet::Type.type(:package).provide :openbsd, :parent 
   has_feature :install_options
   has_feature :uninstall_options
   has_feature :upgradeable
+  has_feature :flavorable
 
+  mk_resource_methods
+
   def self.instances
-    packages = []
+    # our regex for matching pkg_info output
+    regex = /^(.*)-(\d[^-]*)[-]?(\w*)(.*)$/
+    fields = [:name, :ensure, :flavor ]
 
     begin
-      execpipe(listcmd) do |process|
-        # our regex for matching pkg_info output
-        regex = /^(.*)-(\d[^-]*)[-]?(\w*)(.*)$/
-        fields = [:name, :ensure, :flavor ]
-        hash = {}
-
-        # now turn each returned line into a package object
-        process.each_line { |line|
-          if match = regex.match(line.split[0])
-            fields.zip(match.captures) { |field,value|
-              hash[field] = value
-            }
-
-            hash[:provider] = self.name
-
-            packages << new(hash)
-            hash = {}
-          else
-            unless line =~ /Updating the pkgdb/
-              # Print a warning on lines we can't match, but move
-              # on, since it should be non-fatal
-              warning("Failed to match line #{line}")
-            end
+      packages = pkginfo('-a')
+      packages.split("\n").collect do |package|
+        if match = regex.match(package.split[0])
+	  new( :name => match.captures[0],
+               :ensure => match.captures[1],
+               :flavor => match.captures[2],
+          )
+        else
+          unless line =~ /Updating the pkgdb/
+            # Print a warning on lines we can't match, but move
+            # on, since it should be non-fatal
+            warning("Failed to match line #{line}")
           end
-        }
+        end
       end
-
-      return packages
     rescue Puppet::ExecutionFailure
       return nil
     end
   end
 
-  def self.listcmd
-    [command(:pkginfo), "-a"]
+  def self.prefetch(resources)
+    packages = instances
+    resources.keys.each do |name|
+      if provider = packages.find{ |pkg| pkg.name == name }
+        resources[name].provider = provider
+      end
+    end
   end
 
+  def flavor=(value)
+    install
+  end
+
   def latest
     parse_pkgconf
 
@@ -149,31 +150,51 @@ Puppet::Type.type(:package).provide :openbsd, :parent 
 
     if @resource[:source][-1,1] == ::File::SEPARATOR
       e_vars = { 'PKG_PATH' => @resource[:source] }
-      # In case of a real update (i.e., the package already exists) then
-      # pkg_add(8) can handle the flavors. However, if we're actually
-      # installing with 'latest', we do need to handle the flavors.
-      # So we always need to handle flavors ourselves as to not break installs.
-      if latest and resource[:flavor]
-        full_name = "#{resource[:name]}--#{resource[:flavor]}"
-      elsif latest
-        # Don't depend on get_version for updates.
-        full_name = @resource[:name]
-      else
-        full_name = [ @resource[:name], get_version || @resource[:ensure], @resource[:flavor] ].join('-').chomp('-').chomp('-')
-      end
+      full_name = get_full_name(latest)
     else
       e_vars = {}
       full_name = @resource[:source]
     end
 
+    cmd << '-r'
     cmd << install_options
     cmd << full_name
 
     if latest
-      cmd.unshift('-rz')
+      cmd.unshift('-z')
     end
 
-    Puppet::Util.withenv(e_vars) { pkgadd cmd.flatten.compact }
+    # pkg_add(1) doesn't set the return value upon failure so we have to peek
+    # at it's output to see if something went wrong.
+    output = Puppet::Util.withenv(e_vars) { pkgadd cmd.flatten.compact }
+    if output.nil? or output.size == 0 or output =~ /Can't find /
+      self.fail "pkg_add returned: #{output.chomp}"
+    end
+  end
+
+  def get_full_name(latest = false)
+    # In case of a real update (i.e., the package already exists) then
+    # pkg_add(8) can handle the flavors. However, if we're actually
+    # installing with 'latest', we do need to handle the flavors. This is
+    # done so we can feed pkg_add(8) the full package name to install to
+    # prevent ambiguity.
+    if latest && resource[:flavor]
+      "#{resource[:name]}--#{resource[:flavor]}"
+    elsif latest
+      # Don't depend on get_version for updates.
+      @resource[:name]
+    else
+      # If :ensure contains a version, use that instead of looking it up.
+      # This allows for installing packages with the same stem, but multiple
+      # version such as openldap-server.
+      if /(\d[^-]*)$/.match(@resource[:ensure].to_s)
+        use_version = @resource[:ensure]
+      else
+        use_version = get_version
+      end
+
+      [ @resource[:name], use_version, @resource[:flavor]].join('-').gsub(/-+$/, '')
+    end
   end
 
   def get_version
