Skip to content

Commit

Permalink
Revamp OpenBSD package handling
Browse files Browse the repository at this point in the history
 * pkg.conf file is long gone
 * drop versionable
 * drop upgradeable
 * add support for package branches
  • Loading branch information
buzzdeee committed Apr 14, 2024
1 parent ab43286 commit ee87505
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 295 deletions.
191 changes: 50 additions & 141 deletions lib/puppet/provider/package/openbsd.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,56 @@
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

mk_resource_methods

def self.instances
packages = []

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])
next unless match

fields.zip(match.captures) { |field, value|
hash[field] = value
}

hash[:provider] = name

packages << new(hash)
hash = {}
match = regex.match(line.split("\n")[0])
if match
fields.zip(match.captures) { |field, value|
hash[field] = value
}

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

hash[:provider] = name
packages << new(hash)
hash = {}
else
unless line =~ /Updating the pkgdb/
# Print a warning on lines we can't match, but move
# on, since it should be non-fatal
warning(_("Failed to match line %{line}") % { line: line })
end
end
}
end

Expand All @@ -59,168 +66,71 @@ def self.instances
end

def self.listcmd
[command(:pkginfo), "-a"]
end

def latest
if @resource[:flavor]
query = "#{@resource[:name]}--#{@resource[:flavor]}"
else
query = @resource[:name] + "--"
end

output = Puppet::Util.withenv({}) { pkginfo "-Q", query }

if output.nil? or output.size == 0 or output =~ /Error from /
debug "Failed to query for #{resource[:name]}"
return properties[:ensure]
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"
properties[:ensure]
else
match = /^(.*)-(\d[^-]*)-?(\w*)$/.match(output)
debug "Latest available for #{resource[:name]}: #{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
# 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]}"
properties[:ensure]
else
match[2]
end
end
[command(:pkginfo), "-a", "-z"]
end

def update
install(true)
end

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

full_name = get_full_name

cmd << '-r'
cmd << install_options
cmd << get_full_name(latest)

if latest
cmd.unshift('-z')
end
cmd << full_name

# 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 }
pp output
if output =~ /Can't find /
self.fail "pkg_add returned: #{output.chomp}"
end
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 resource[:flavor]
# 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 postfix-VERSION-flavor.
if @resource[:ensure].to_s =~ /(\d[^-]*)$/
use_version = @resource[:ensure]
else
use_version = ''
end
"#{resource[:name]}-#{use_version}-#{resource[:flavor]}"
elsif resource[:name].to_s.match(/[a-z0-9]%[0-9a-z]/i)
resource[:name].to_s
elsif !latest
"#{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

if resource[:flavor]
[@resource[:name], use_version, @resource[:flavor]].join('-').gsub(/-+$/, '')
else
[@resource[:name], use_version]
end
end
end

def get_version
pkg_search_name = @resource[:name]
unless pkg_search_name.match(/[a-z0-9]%[0-9a-z]/i) and !@resource[:flavor]
# we are only called when no flavor is specified
# so append '--' to the :name to avoid patch versions on flavors
pkg_search_name << "--"
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
# our regex for matching pkg_info output
regex = /^(.*)-(\d[^-]*)[-]?(\w*)(.*)$/
master_version = 0
version = -1

# pkg_info -I might return multiple lines, i.e. flavors
matching_pkgs = pkginfo("-I", "pkg_search_name")
matching_pkgs.each_line do |line|
next unless (match = regex.match(line.split[0]))

# 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
if @resource[:flavor]
"#{use_name}--#{@resource[:flavor]}#{use_branch}"
else
"#{use_name}--#{use_branch}"
end

return master_version unless master_version == 0

return '' if version == -1

raise Puppet::Error, _("%{version} is not available for this package") % { version: version }
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 @@ -229,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--
1 change: 0 additions & 1 deletion spec/fixtures/unit/provider/package/openbsd/pkginfo.query

This file was deleted.

This file was deleted.

Loading

0 comments on commit ee87505

Please sign in to comment.