Skip to content
Open
196 changes: 39 additions & 157 deletions lib/puppet/provider/package/openbsd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,24 @@
Puppet::Type.type(:package).provide :openbsd, :parent => Puppet::Provider::Package do
desc "OpenBSD's form of `pkg_add` support.

OpenBSD has the concept of package branches, providing multiple versions of the
same package, i.e. `stable` vs. `snapshot`. To select a specific branch,
suffix the package name with % sign follwed by the branch name, i.e. `gimp%stable`.

This provider supports the `install_options` and `uninstall_options`
attributes, which allow command-line flags to be passed to pkg_add and pkg_delete.
These options should be specified as an array where each element is either a
string or a hash."

commands :pkginfo => "pkg_info",
:pkgadd => "pkg_add",
commands :pkgadd => "pkg_add",
:pkginfo => "pkg_info",
:pkgdelete => "pkg_delete"

defaultfor 'os.name' => :openbsd
confine 'os.name' => :openbsd

has_feature :versionable
has_feature :install_options
has_feature :uninstall_options
has_feature :upgradeable
has_feature :supports_flavors

def self.instances
Expand All @@ -30,20 +32,21 @@ def self.instances
begin
execpipe(listcmd) do |process|
# our regex for matching pkg_info output
regex = /^(.*)-(\d[^-]*)-?([\w-]*)(.*)$/
fields = [:name, :ensure, :flavor]
regex = /^(.*)--([\w-]+)?(%[^w]+)?$/
fields = [:name, :flavor, :branch]
hash = {}

# now turn each returned line into a package object
process.each_line { |line|
match = regex.match(line.split[0])
match = regex.match(line.split("\n")[0])
if match
fields.zip(match.captures) { |field, value|
hash[field] = value
}

hash[:provider] = name
hash[:name] = "#{hash[:name]}#{hash[:branch]}" if hash[:branch]

hash[:provider] = name
packages << new(hash)
hash = {}
else
Expand All @@ -63,191 +66,71 @@ def self.instances
end

def self.listcmd
[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 }
version = properties[:ensure]

if output.nil? or output.size == 0 or output =~ /Error from /
debug "Failed to query for #{resource[:name]}"
return version
else
# Remove all fuzzy matches first.
output = output.split.select { |p| p =~ /^#{resource[:name]}-(\d[^-]*)-?(\w*)/ }.join
debug "pkg_info -Q for #{resource[:name]}: #{output}"
end

if output =~ /^#{resource[:name]}-(\d[^-]*)-?(\w*) \(installed\)$/
debug "Package is already the latest available"
version
else
match = /^(.*)-(\d[^-]*)-?(\w*)$/.match(output)
debug "Latest available for #{resource[:name]}: #{match[2]}"

if version.to_sym == :absent || version.to_sym == :purged
return match[2]
end

vcmp = version.split('.').map(&:to_i) <=> match[2].split('.').map(&:to_i)
if vcmp > 0
# 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]} #{version} newer then available #{match[2]}"
version
else
match[2]
end
end
[command(:pkginfo), "-a", "-z"]
end

def update
install(true)
end

def parse_pkgconf
unless @resource[:source]
if Puppet::FileSystem.exist?("/etc/pkg.conf")
File.open("/etc/pkg.conf", "rb").readlines.each do |line|
matchdata = line.match(/^installpath\s*=\s*(.+)\s*$/i)
if matchdata
@resource[:source] = matchdata[1]
else
matchdata = line.match(/^installpath\s*\+=\s*(.+)\s*$/i)
if matchdata
if @resource[:source].nil?
@resource[:source] = matchdata[1]
else
@resource[:source] += ":" + matchdata[1]
end
end
end
end

unless @resource[:source]
raise Puppet::Error,
_("No valid installpath found in /etc/pkg.conf and no source was set")
end
else
raise Puppet::Error,
_("You must specify a package source or configure an installpath in /etc/pkg.conf")
end
end
end

def install(latest = false)
def install
cmd = []

parse_pkgconf

if @resource[:source][-1, 1] == ::File::SEPARATOR
e_vars = { 'PKG_PATH' => @resource[:source] }
full_name = get_full_name(latest)
else
e_vars = {}
full_name = @resource[:source]
end
full_name = get_full_name

cmd << '-r'
cmd << install_options
cmd << full_name

if latest
cmd.unshift('-rz')
# 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({}) { pkgadd cmd.flatten.compact }
if output =~ /Can't find /
self.fail "pkg_add returned: #{output.chomp}"
end

Puppet::Util.withenv(e_vars) { pkgadd cmd.flatten.compact }
end

def get_full_name(latest = false)
def get_full_name
# 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 @resource[:ensure].to_s =~ /(\d[^-]*)$/
use_version = @resource[:ensure]
else
use_version = get_version
end

[@resource[:name], use_version, @resource[:flavor]].join('-').gsub(/-+$/, '')
name_branch_regex = /^(\S*)(%\w*)$/
match = name_branch_regex.match(@resource[:name])
if match
use_name = match.captures[0]
use_branch = match.captures[1]
else
use_name = @resource[:name]
use_branch = ''
end
end

def get_version
execpipe([command(:pkginfo), "-I", @resource[:name]]) do |process|
# our regex for matching pkg_info output
regex = /^(.*)-(\d[^-]*)-?(\w*)(.*)$/
master_version = 0
version = -1

process.each_line do |line|
match = regex.match(line.split[0])
next unless match

# now we return the first version, unless ensure is latest
version = match.captures[1]
return version unless @resource[:ensure] == "latest"

master_version = version unless master_version > version
end

return master_version unless master_version == 0
return '' if version == -1

raise Puppet::Error, _("%{version} is not available for this package") % { version: version }
if @resource[:flavor]
"#{use_name}--#{@resource[:flavor]}#{use_branch}"
else
"#{use_name}--#{use_branch}"
end
rescue Puppet::ExecutionFailure
nil
end

def query
# Search for the version info
if pkginfo(@resource[:name]) =~ /Information for (inst:)?#{@resource[:name]}-(\S+)/
{ :ensure => Regexp.last_match(2) }
else
nil
pkg = self.class.instances.find do |package|
@resource[:name] == package.name
end
pkg ? pkg.properties : nil
end

def install_options
join_options(resource[:install_options])
end

def uninstall_options
join_options(resource[:uninstall_options])
join_options(resource[:uninstall_options]) || []
end

def uninstall
pkgdelete uninstall_options.flatten.compact, @resource[:name]
pkgdelete uninstall_options.flatten.compact, get_full_name
end

def purge
pkgdelete "-c", "-q", @resource[:name]
pkgdelete "-c", "-qq", uninstall_options.flatten.compact, get_full_name
end

def flavor
Expand All @@ -256,7 +139,6 @@ def flavor

def flavor=(value)
if flavor != @resource.should(:flavor)
uninstall
install
end
end
Expand Down
19 changes: 0 additions & 19 deletions spec/fixtures/unit/provider/package/openbsd/pkginfo.detail

This file was deleted.

16 changes: 6 additions & 10 deletions spec/fixtures/unit/provider/package/openbsd/pkginfo.list
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
bash-3.1.17 GNU Bourne Again Shell
bzip2-1.0.3 block-sorting file compressor, unencumbered
expat-2.0.0 XML 1.0 parser written in C
gettext-0.14.5p1 GNU gettext
libiconv-1.9.2p3 character set conversion library
lzo-1.08p1 portable speedy lossless data compression library
openvpn-2.0.6 easy-to-use, robust, and highly configurable VPN
python-2.4.3p0 interpreted object-oriented programming language
vim-7.0.42-no_x11 vi clone, many additional features
wget-1.10.2p0 retrieve files from the web via HTTP, HTTPS and FTP
autoconf--%2.13
autoconf--%2.56
bash--
postfix--ldap%stable
puppet--%8
zstd--

This file was deleted.

This file was deleted.

Loading