$OpenBSD: patch-lib_puppet_provider_package_openbsd_rb,v 1.22 2014/07/10 22:13:09 jasper Exp $

From 3637c48d97e4ed9fddbad883cc1cce571270e187 Mon Sep 17 00:00:00 2001
From: Jasper Lievisse Adriaanse <jasper@humppa.nl>
Date: Mon, 21 Apr 2014 20:02:27 +0200
Subject: [PATCH] (PUP-1069) Implement feature :upgradeable for OpenBSD package  provider.

From e59b8a725e1b6324c9c4db81d0017f76b924eb88 Mon Sep 17 00:00:00 2001
From: Jasper Lievisse Adriaanse <jasper@humppa.nl>
Date: Tue, 22 Apr 2014 00:40:25 +0200
Subject: [PATCH] (PUP-2311) OpenBSD uninstall broken when multiple uninstall_options given

--- lib/puppet/provider/package/openbsd.rb.orig	Mon Jun  9 23:08:19 2014
+++ lib/puppet/provider/package/openbsd.rb	Thu Jul 10 16:23:31 2014
@@ -4,7 +4,9 @@ require 'puppet/provider/package'
 Puppet::Type.type(:package).provide :openbsd, :parent => Puppet::Provider::Package do
   desc "OpenBSD's form of `pkg_add` support."
 
-  commands :pkginfo => "pkg_info", :pkgadd => "pkg_add", :pkgdelete => "pkg_delete"
+  commands :pkginfo   => "pkg_info",
+           :pkgadd    => "pkg_add",
+           :pkgdelete => "pkg_delete"
 
   defaultfor :operatingsystem => :openbsd
   confine :operatingsystem => :openbsd
@@ -12,6 +14,7 @@ Puppet::Type.type(:package).provide :openbsd, :parent 
   has_feature :versionable
   has_feature :install_options
   has_feature :uninstall_options
+  has_feature :upgradeable
 
   def self.instances
     packages = []
@@ -54,6 +57,60 @@ Puppet::Type.type(:package).provide :openbsd, :parent 
     [command(:pkginfo), "-a"]
   end
 
+  def latest
+    parse_pkgconf
+
+    if @resource[:source][-1,1] == ::File::SEPARATOR
+      e_vars = { 'PKG_PATH' => @resource[:source] }
+    else
+      e_vars = {}
+    end
+
+    if @resource[:flavor]
+      query = "#{@resource[:name]}--#{@resource[:flavor]}"
+    else
+      query = @resource[:name]
+    end
+
+    output = Puppet::Util.withenv(e_vars) {pkginfo "-Q", query}.split('\n')
+
+    if output.nil? or output.size == 0
+      debug "Failed to query for #{query}"
+      return properties[:ensure]
+    else
+      # Remove all fuzzy matches first.
+      output = output.select {|p| p =~ /^#{resource[:name]}-(\d[^-]*)[-]?(\w*)/}.join.chomp
+      debug "pkg_info -Q for #{query}: #{output}"
+    end
+
+    if output =~ /^#{resource[:name]}-(\d[^-]*)[-]?(\w*)(-#{resource[:flavor]})? \(installed\)$/
+      debug "Package is already the latest available"
+      return properties[:ensure]
+    else
+      # Flavors need a little nudge in the right direction
+      match = /(^#{resource[:name]})-(\d[^-]*)[-]?(\w*)(-#{resource[:flavor]})?/.match(output)
+      debug "Latest available for #{query}: #{match[2]}"
+      if properties[:ensure].to_sym == :absent
+        return match[2]
+      end
+
+      vcmp = properties[:ensure].split('.').map{|s|s.to_i} <=> match[2].split('.').map{|s|s.to_i}
+      if vcmp > 0
+        debug "ensure: #{properties[:ensure]}"
+        # The locally installed package may actually be newer than what a mirror
+        # has. Log it at debug, but ignore it otherwise.
+        debug "Package #{resource[:name]} #{properties[:ensure]} newer then available #{match[2]}"
+        return properties[:ensure]
+      else
+        return match[2]
+      end
+    end
+  end
+
+  def update
+    self.install(true)
+  end
+
   def parse_pkgconf
     unless @resource[:source]
       if Puppet::FileSystem.exist?("/etc/pkg.conf")
@@ -80,14 +137,25 @@ Puppet::Type.type(:package).provide :openbsd, :parent 
     end
   end
 
-  def install
+  def install(latest = false)
     cmd = []
 
     parse_pkgconf
 
     if @resource[:source][-1,1] == ::File::SEPARATOR
       e_vars = { 'PKG_PATH' => @resource[:source] }
-      full_name = [ @resource[:name], get_version || @resource[:ensure], @resource[:flavor] ].join('-').chomp('-').chomp('-')
+      # 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
     else
       e_vars = {}
       full_name = @resource[:source]
@@ -96,16 +164,19 @@ Puppet::Type.type(:package).provide :openbsd, :parent 
     cmd << install_options
     cmd << full_name
 
-    Puppet::Util.withenv(e_vars) { pkgadd cmd.flatten.compact.join(' ') }
+    if latest
+      cmd.unshift('-rz')
+    end
+
+    Puppet::Util.withenv(e_vars) { pkgadd cmd.flatten.compact }
   end
 
   def get_version
     execpipe([command(:pkginfo), "-I", @resource[:name]]) do |process|
       # our regex for matching pkg_info output
-      regex = /^(.*)-(\d[^-]*)[-]?(\D*)(.*)$/
+      regex = /^(.*)-(\d[^-]*)[-]?(\w*)(.*)$/
       master_version = 0
       version = -1
-
       process.each_line do |line|
         if match = regex.match(line.split[0])
           # now we return the first version, unless ensure is latest
@@ -142,7 +213,7 @@ Puppet::Type.type(:package).provide :openbsd, :parent 
   end
 
   def uninstall
-    pkgdelete uninstall_options.flatten.compact.join(' '), @resource[:name]
+    pkgdelete uninstall_options.flatten.compact, @resource[:name]
   end
 
   def purge
