Backstory: There are 2 package providers supplied for freebsd - one
for freebsd packages (:freebsd), and one for ports (:ports).
Freebsd's ports is the most commonly used system for freebsd as it
contains instructions and patches for compiling software from source.
The current package :ports utilizes a common 3rd party software
(portupgrade) system to manage (install/version/etc) the ports system
which remains unchanged. This ticket/code is to replace that
provider.
":ports" provider contains a few issues that are addressed in this
replacement
* ensure=>latest is broken
* unable to deal with multi-versions of a software port (eg: mysql50,
mysql51, etc)
* uninstall can fail
Incompatible change with this new package:
* package name needs to be the full port origin (eg: databases/mysql51-
client)
Configuration example:
package { "shells/bash":
provider => portupgrade,
alias => "bash",
ensure => latest,
}
Signed-off-by: Ross West <[email protected]>
---
diff --git a/lib/puppet/provider/package/portupgrade.rb b/lib/puppet/
provider/package/portupgrade.rb
new file mode 100644
index 0000000..531cab2
--- /dev/null
+++ b/lib/puppet/provider/package/portupgrade.rb
@@ -0,0 +1,251 @@
+
+# Whole new package, so include pack stuff
+require 'puppet/provider/package'
+
+Puppet::Type.type(:package).provide :portupgrade, :parent =>
Puppet::Provider::Package do
+ include Puppet::Util::Execution
+
+ desc "Support for FreeBSD's ports using the portupgrade ports
management software.
+ Use the port's full origin as the resource name. eg
(ports-mgmt/portupgrade)
+ for the portupgrade port."
+
+ ## has_features is usually autodetected based on defs below.
+ # has_features :installable, :uninstallable, :upgradeable
+
+ commands :portupgrade => "/usr/local/sbin/portupgrade",
+ :portinstall => "/usr/local/sbin/portinstall",
+ :portversion => "/usr/local/sbin/portversion",
+ :portuninstall => "/usr/local/sbin/pkg_deinstall",
+ :portinfo => "/usr/sbin/pkg_info"
+
+ ## Activate this only once approved by someone important.
+ # defaultfor :operatingsystem => :freebsd
+
+ # Remove unwanted environment variables.
+ %w{INTERACTIVE UNAME}.each do |var|
+ if ENV.include?(var)
+ ENV.delete(var)
+ end
+ end
+
+ ######## instances sub command (builds the installed packages
list)
+
+ def self.instances
+ Puppet.debug "portupgrade.rb Building packages list
from installed ports"
+
+ # regex to match output from pkg_info
+ regex = %r{^(\S+)-([^-\s]+):(\S+)$}
+ # Corresponding field names
+ fields = [:portname, :ensure, :portorigin]
+ # define Temporary hash used, packages array of hashes
+ hash = Hash.new
+ packages = []
+
+ # exec command
+ cmdline = ["-aoQ"]
+ begin
+ output = portinfo(*cmdline)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error.new(output)
+ return nil
+ end
+
+ # split output and match it and populate temp hash
+ output.split("\n").each { |data|
+ # reset hash to nil for each line
+ hash.clear
+ if match = regex.match(data)
+ # Output matched regex
+ fields.zip(match.captures) { |field,
value|
+ hash[field] = value
+ }
+
+ # populate the actual :name field from
the :portorigin
+ # Set :provider to this object name
+ hash[:name] = hash[:portorigin]
+ hash[:provider] = self.name
+
+ # Add to the full packages listing
+ packages << new(hash)
+
+ else
+ # unrecognised output from pkg_info
+ Puppet.debug "portupgrade.Instances()
- unable to match output: %s" % data
+ end
+ }
+
+ # return the packages array of hashes
+ return packages
+
+ end
+
+ ######## Installation sub command
+
+ def install
+ Puppet.debug "portupgrade.install() - Installation
call on %s" % @resource[:name]
+ # -M: yes, we're a batch, so don't ask any questions
+ cmdline = ["-M BATCH=yes", @resource[:name]]
+
+ # FIXME: it's possible that portinstall prompts for
data so locks up.
+ begin
+ output = portinstall(*cmdline)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error.new(output)
+ end
+
+ if output =~ /\*\* No such /
+ raise Puppet::ExecutionFailure, "Could not
find package %s" % @resource[:name]
+ end
+
+ # No return code required, so do nil to be clean
+ return nil
+ end
+
+ ######## Latest subcommand (returns the latest version
available, or current version if installed is latest)
+
+ def latest
+ Puppet.debug "portupgrade.latest() - Latest check
called on %s" % @resource[:name]
+ # search for latest version available, or return
current version.
+ # cmdline = "portversion -v <portorigin>", returns
"<portname> <code> <stuff>"
+ # or "** No matching package found: <portname>"
+ cmdline = ["-v", @resource[:name]]
+
+ begin
+ output = portversion(*cmdline)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error.new(output)
+ end
+
+ # Check: output format.
+ if output =~ /^\S+-([^-\s]+)\s+(\S)\s+(.*)/
+ # $1 = installed version, $2 = comparison, $3
other data
+ # latest installed
+ installedversion = $1
+ comparison = $2
+ otherdata = $3
+
+ # Only return a new version number when it's
clear that there is a new version
+ # all others return the current version so no
unexpected 'upgrades' occur.
+ case comparison
+ when "=", ">"
+ Puppet.debug "portupgrade.latest() -
Installed package is latest (%s)" % installedversion
+ return installedversion
+ when "<"
+ # "portpkg-1.7_5 < needs updating
(port has 1.14)"
+ # "portpkg-1.7_5 < needs updating
(port has 1.14) (=> 'newport/pkg')
+ if otherdata =~ /\(port has (\S+)\)/
+ newversion = $1
+ Puppet.debug
"portupgrade.latest() - Installed version needs updating to (%s)" %
newversion
+ return newversion
+ else
+ Puppet.debug
"portupgrade.latest() - Unable to determine new version from (%s)" %
otherdata
+ return installedversion
+ end
+ when "?", "!", "#"
+ Puppet.debug "portupgrade.latest() -
Comparison Error reported from portversion (%s)" % output
+ return installedversion
+ else
+ Puppet.debug "portupgrade.latest() -
Unknown code from portversion output (%s)" % output
+ return installedversion
+ end
+
+ else
+ # error: output not parsed correctly, error
out with nil.
+ # Seriously - this section should never be
called in a perfect world.
+ # as verification that the port is installed
has already happened in query.
+ if output =~ /^\*\* No matching package /
+ raise Puppet::ExecutionFailure, "Could
not find package %s" % @resource[:name]
+ else
+ # Any other error (dump output to log)
+ raise Puppet::ExecutionFailure,
"Unexpected output from portversion: %s" % output
+ end
+
+ # Just in case we still are running, return
nil
+ return nil
+ end
+
+ # At this point normal operation has finished and we
shouldn't have been called.
+ # Error out and let the admin deal with it.
+ raise Puppet::Error, "portversion.latest() - fatal
error with portversion: %s" % output
+ return nil
+
+ end
+
+ ###### Query subcommand - return a hash of details if exists,
or nil if it doesn't.
+ # Used to make sure the package is installed
+
+ def query
+ Puppet.debug "portupgrade.query() - Called on %s" %
@resource[:name]
+
+ cmdline = ["-qO", @resource[:name]]
+ begin
+ output = portinfo(*cmdline)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error.new(output)
+ end
+
+ # Check: if output isn't in the right format, return
nil
+ if output =~ /^(\S+)-([^-\s]+)/
+ # Fill in the details
+ hash = Hash.new
+ hash[:portorigin] = self.name
+ hash[:portname] = $1
+ hash[:ensure] = $2
+
+ # If more details are required, then we can do
another pkg_info query here
+ # and parse out that output and add to the
hash
+
+ # return the hash to the caller
+ return hash
+ else
+ Puppet.debug "portupgrade.query() - package
(%s) not installed" % @resource[:name]
+ return nil
+ end
+
+ end # def query
+
+ ####### Uninstall command
+
+ def uninstall
+ Puppet.debug "portupgrade.uninstall() - called on %s"
% @resource[:name]
+ # Get full package name from port origin to uninstall
with
+ cmdline = ["-qO", @resource[:name]]
+ begin
+ output = portinfo(*cmdline)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error.new(output)
+ end
+
+ if output =~ /^(\S+)/
+ # output matches, so uninstall it
+ portuninstall $1
+ end
+
+ end
+
+ ######## Update/upgrade command
+
+ def update
+ Puppet.debug "portupgrade.update() - called on (%s)" %
@resource[:name]
+
+ cmdline = ["-qO", @resource[:name]]
+ begin
+ output = portinfo(*cmdline)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error.new(output)
+ end
+
+ if output =~ /^(\S+)/
+ # output matches, so upgrade the software
+ cmdline = ["-M BATCH=yes", $1]
+ begin
+ output = portupgrade(*cmdline)
+ rescue Puppet::ExecutionFailure
+ raise Puppet::Error.new(output)
+ end
+ end
+ end
+
+## EOF
+end
+
--
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.