* Use port origin as resource name
 * Use port index from package site to determine package to use for a
   given origin
 * Promote as default package provider. Demote portupgrade provider.

Signed-off-by: Russell Jackson <[email protected]>
---
 lib/puppet/provider/package/freebsd.rb |  161 +++++++++++++++++++++++++++-----
 lib/puppet/provider/package/ports.rb   |    2 -
 2 files changed, 137 insertions(+), 26 deletions(-)

diff --git a/lib/puppet/provider/package/freebsd.rb 
b/lib/puppet/provider/package/freebsd.rb
index e10a20b..e0045ac 100755
--- a/lib/puppet/provider/package/freebsd.rb
+++ b/lib/puppet/provider/package/freebsd.rb
@@ -1,37 +1,150 @@
-Puppet::Type.type(:package).provide :freebsd, :parent => :openbsd do
-  desc "The specific form of package management on FreeBSD.  This is an
-    extremely quirky packaging system, in that it freely mixes between
-    ports and packages.  Apparently all of the tools are written in Ruby,
-    so there are plans to rewrite this support to directly use those
-    libraries."
+require 'open-uri'
 
-  commands :pkginfo => "/usr/sbin/pkg_info",
-    :pkgadd => "/usr/sbin/pkg_add",
-    :pkgdelete => "/usr/sbin/pkg_delete"
+Puppet::Type.type(:package).provide :freebsd, :parent => 
Puppet::Provider::Package do
+  include Puppet::Util::Execution
+
+  desc "The specific form of package management on FreeBSD. Resource names 
must be
+  specified as the port origin: <port_category>/<port_name>."
+
+  commands :pkginfo    => "/usr/sbin/pkg_info",
+           :pkgadd     => "/usr/sbin/pkg_add",
+           :pkgdelete  => "/usr/sbin/pkg_delete"
 
   confine :operatingsystem => :freebsd
+  defaultfor :operatingsystem => :freebsd
 
-  def self.listcmd
-    command(:pkginfo)
+  @@lock = Mutex.new
+  @@ports_index = nil
+
+  def self.parse_pkg_string(pkg_string)
+    {
+      :pkg_name => pkg_string.split("-").slice(0..-2).join("-"),
+      :pkg_version => pkg_string.split("-")[-1],
+    }
   end
 
-  def install
-    should = @resource.should(:ensure)
+  def self.unparse_pkg_info(pkg_info)
+    [:pkg_name, :pkg_version].map { |key| pkg_info[key] }.join("-")
+  end
+  
+  def self.parse_origin(origin_path)
+    begin
+      origin = {
+        :port_category => origin_path.split("/").fetch(-2),
+        :port_name     => origin_path.split("/").fetch(-1),
+      }
+    rescue IndexError
+      raise Puppet::Error.new "#{origin_path}: not in required origin format: 
.*/<port_category>/<port_name>"
+    end
+    origin
+  end
 
-    if @resource[:source] =~ /\/$/
-      if @resource[:source] =~ /^(ftp|https?):/
-        Puppet::Util::Execution::withenv :PACKAGESITE => @resource[:source] do
-          pkgadd "-r", @resource[:name]
+  def self.instances
+    packages = []
+    output = pkginfo "-aoQ"
+    output.split("\n").each do |data|
+      pkg_string, pkg_origin = data.split(":")
+      pkg_info = self.parse_pkg_string(pkg_string)
+
+      packages << new({
+        :provider => self.name,
+        :name     => pkg_origin,
+        :ensure   => pkg_info[:pkg_version],
+      })
+    end
+    packages
+  end
+
+  def ports_index
+    @@lock.synchronize do
+      if @@ports_index.nil?
+        @@ports_index = {}
+        uri = source.merge File.join(source.path, "/INDEX")
+        Puppet.debug "Fetching INDEX: #{uri.inspect}"
+        begin
+          open(uri, "r") do |f|
+            while (line = f.gets)
+              fields = line.split("|")
+              pkg_info = self.class.parse_pkg_string(fields[0])
+              origin = self.class.parse_origin(fields[1])
+              @@ports_index[origin] = pkg_info
+            end
+          end
+        rescue
+          @@ports_index = nil
+          raise Puppet::Error.new "Could not fetch ports INDEX"
         end
-      else
-        Puppet::Util::Execution::withenv :PKG_PATH => @resource[:source] do
-          pkgadd @resource[:name]
+      end
+    end
+    @@ports_index
+  end
+
+  def uri_path
+    Facter.loadfacts
+    File.join(
+      "/", "pub", "FreeBSD", "ports",
+      Facter.value(:hardwareisa),
+      [
+        "packages",
+        Facter.value(:kernelmajversion).split(".")[0],
+        "stable",
+      ].join("-")
+    )
+  end
+
+  def source
+    if !defined? @source
+      if @resource[:source]
+        @source = URI.parse(@resource[:source])
+        if [email protected]?
+          @source.merge! uri_path
         end
+      else # source parameter not set; build default source URI
+        @source = URI::Generic.build({
+          :scheme => "ftp",
+          :host => "ftp.freebsd.org",
+          :path => uri_path,
+        })
       end
+      Puppet.debug "Package: #{@resource[:name]}: source => #{@source.inspect}"
+    end
+    @source
+  end
+
+  def origin
+    if !defined? @origin
+      @origin = self.class.parse_origin(@resource[:name])
+      Puppet.debug "Package: #{@resource[:name]}: origin => #{@origin.inspect}"
+    end
+    @origin
+  end
+
+  def package_uri
+    begin
+      pkg_name = self.class.unparse_pkg_info(ports_index.fetch(origin))
+    rescue IndexError
+      raise Puppet::Error.new "package not found in INDEX"
+    end
+    uri = source.merge File.join(source.path, "All", pkg_name + ".tbz")
+    Puppet.debug "Package: #{@resource[:name]}: package_uri => #{uri.inspect}"
+    uri
+  end
+
+  def install
+    should = @resource.should(:ensure)
+    origin # call origin so we check the package name for correctness early
+
+    # Source URI is for local file path.
+    if !source.absolute? or source.scheme == "file"
+      pkgadd source.path
+    # Source URI is to specific package file
+    elsif source.absolute? && source.path.end_with?(".tbz")
+      pkgadd source.to_s
+    # Source URI is to a package repository
     else
-      Puppet.warning "source is defined but does not have trailing slash, 
ignoring #{@resource[:source]}" if @resource[:source]
-      pkgadd "-r", @resource[:name]
+      pkgadd "-f", package_uri.to_s
     end
+    nil
   end
 
   def query
@@ -44,7 +157,7 @@ Puppet::Type.type(:package).provide :freebsd, :parent => 
:openbsd do
   end
 
   def uninstall
-    pkgdelete "#{@resource[:name]}-#{@resource.should(:ensure)}"
+    output = pkginfo "-qO", @resource[:name]
+    output.split("\n").each { |pkg_name| pkgdelete([pkg_name]) }
   end
 end
-
diff --git a/lib/puppet/provider/package/ports.rb 
b/lib/puppet/provider/package/ports.rb
index c802092..1f2ed43 100755
--- a/lib/puppet/provider/package/ports.rb
+++ b/lib/puppet/provider/package/ports.rb
@@ -6,8 +6,6 @@ Puppet::Type.type(:package).provide :ports, :parent => 
:freebsd, :source => :fre
     :portuninstall => "/usr/local/sbin/pkg_deinstall",
     :portinfo => "/usr/sbin/pkg_info"
 
-  defaultfor :operatingsystem => :freebsd
-
   # I hate ports
   %w{INTERACTIVE UNAME}.each do |var|
     ENV.delete(var) if ENV.include?(var)
-- 
1.7.0.4

-- 
You received this message because you are subscribed to the Google Groups 
"Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/puppet-dev?hl=en.

Reply via email to