Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bring OpenBSD package handling up to date to what is in the #9301

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
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