Skip to content

Commit

Permalink
(PUP-7095) Enable portage install and uninstall options
Browse files Browse the repository at this point in the history
Allow the portage package provider to accept install and uninstall
options.  This is useful if you do not want to use the default global
EMERGE_DEFAULT_OPTS paramaters setable in make.conf.

Allow the portage package provider to purge package if requested.

Allow package sets (@world @System and the like)
  • Loading branch information
prometheanfire committed Mar 20, 2017
1 parent 57da494 commit 3462fd4
Show file tree
Hide file tree
Showing 2 changed files with 257 additions and 71 deletions.
212 changes: 156 additions & 56 deletions lib/puppet/provider/package/portage.rb
Expand Up @@ -2,14 +2,19 @@
require 'fileutils'

Puppet::Type.type(:package).provide :portage, :parent => Puppet::Provider::Package do
desc "Provides packaging support for Gentoo's portage system."
desc "Provides packaging support for Gentoo's portage system.
has_features :versionable, :reinstallable
This provider supports the `install_options` and `uninstall_options` attributes, which allows command-line
flags to be passed to emerge. These options should be specified as a string (e.g. '--flag'), a hash
(e.g. {'--flag' => 'value'}), or an array where each element is either a string or a hash."

has_features :install_options, :purgeable, :reinstallable, :uninstall_options, :versionable, :virtual_packages

{
:emerge => "/usr/bin/emerge",
:eix => "/usr/bin/eix",
:update_eix => "/usr/bin/eix-update",
:emerge => '/usr/bin/emerge',
:eix => '/usr/bin/eix',
:qatom_bin => '/usr/bin/qatom',
:update_eix => '/usr/bin/eix-update',
}.each_pair do |name, path|
has_command(name, path) do
environment :HOME => '/'
Expand All @@ -24,15 +29,18 @@ def self.instances
result_format = self.eix_result_format
result_fields = self.eix_result_fields

limit = self.eix_limit
version_format = self.eix_version_format
slot_versions_format = self.eix_slot_versions_format
installed_versions_format = self.eix_installed_versions_format
installable_versions_format = self.eix_install_versions_format
begin
eix_file = File.directory?("/var/cache/eix") ? "/var/cache/eix/portage.eix" : "/var/cache/eix"
eix_file = File.directory?('/var/cache/eix') ? '/var/cache/eix/portage.eix' : '/var/cache/eix'
update_eix if !FileUtils.uptodate?(eix_file, %w{/usr/bin/eix /usr/portage/metadata/timestamp})

search_output = nil
Puppet::Util.withenv :LASTVERSION => version_format, :LASTSLOTVERSIONS => slot_versions_format do
search_output = eix *(self.eix_search_arguments + ["--installed"])
Puppet::Util.withenv :EIX_LIMIT => limit, :LASTVERSION => version_format, :LASTSLOTVERSIONS => slot_versions_format, :INSTALLEDVERSIONS => installed_versions_format, :STABLEVERSIONS => installable_versions_format do
search_output = eix *(self.eix_search_arguments + ['--installed'])
end

packages = []
Expand All @@ -57,65 +65,118 @@ def self.instances

def install
should = @resource.should(:ensure)
name = package_name
unless should == :present or should == :latest
# We must install a specific version
name = package_atom_with_version(should)
end
emerge name
cmd = %w{}
name = qatom[:category] ? "#{qatom[:category]}/#{qatom[:pn]}" : qatom[:pn]
name = qatom[:pfx] + name if qatom[:pfx]
name = name + '-' + qatom[:pv] if qatom[:pv]
name = name + '-' + qatom[:pr] if qatom[:pr]
name = name + qatom[:slot] if qatom[:slot]
cmd << '--update' if [:latest].include?(should)
cmd += install_options if @resource[:install_options]
cmd << name
emerge *cmd
end

# The common package name format.
def package_name
@resource[:category] ? "#{@resource[:category]}/#{@resource[:name]}" : @resource[:name]
def uninstall
should = @resource.should(:ensure)
cmd = %w{--unmerge --rage-clean}
name = qatom[:category] ? "#{qatom[:category]}/#{qatom[:pn]}" : qatom[:pn]
name = qatom[:pfx] + name if qatom[:pfx]
name = name + '-' + qatom[:pv] if qatom[:pv]
name = name + '-' + qatom[:pr] if qatom[:pr]
name = name + qatom[:slot] if qatom[:slot]
cmd += uninstall_options if @resource[:uninstall_options]
cmd << name
if [:purged].include?(should)
Puppet::Util.withenv :CONFIG_PROTECT => "-*" do
emerge *cmd
end
else
emerge *cmd
end
end

def package_name_without_slot
package_name.sub(self.class.slot_pattern, '')
def reinstall
self.install
end

def package_slot
if match = package_name.match(self.class.slot_pattern)
match[1]
end
def update
self.install
end

def package_atom_with_version(version)
if slot = package_slot
"=#{package_name_without_slot}-#{version}:#{package_slot}"
else
"=#{package_name}-#{version}"
def qatom
output_format = self.qatom_output_format
result_format = self.qatom_result_format
result_fields = self.qatom_result_fields
begin
search_output = nil
package_info = {}
# do the search
search_output = qatom_bin *([@resource[:name], '--format', output_format])
# verify if the search found anything
match = result_format.match(search_output)
if match
result_fields.zip(match.captures) do |field, value|
# some fields can be empty or (null) (if we are not passed a category in the package name for instance)
if value == '(null)'
package_info[field] = nil
elsif !value or value.empty?
package_info[field] = nil
else
package_info[field] = value
end
end
end
return package_info
rescue Puppet::ExecutionFailure => detail
raise Puppet::Error.new(detail)
end
end

def uninstall
emerge "--unmerge", package_name
def qatom_output_format
'"[%{CATEGORY}] [%{PN}] [%{PV}] [%[PR]] [%[SLOT]] [%[pfx]] [%[sfx]]"'
end

def reinstall
self.install
def qatom_result_format
/^\"\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\](.*)\"$/
end

def update
self.install
def qatom_result_fields
[:category, :pn, :pv, :pr, :slot, :pfx, :sfx]
end

def query
limit = self.class.eix_limit
result_format = self.class.eix_result_format
result_fields = self.class.eix_result_fields

version_format = self.class.eix_version_format
slot_versions_format = self.class.eix_slot_versions_format
search_field = package_name_without_slot.count('/') > 0 ? "--category-name" : "--name"
search_value = package_name_without_slot
installed_versions_format = self.class.eix_installed_versions_format
installable_versions_format = self.class.eix_install_versions_format
search_field = qatom[:category] ? '--category-name' : '--name'
search_value = qatom[:category] ? "#{qatom[:category]}/#{qatom[:pn]}" : qatom[:pn]

begin
eix_file = File.directory?("/var/cache/eix") ? "/var/cache/eix/portage.eix" : "/var/cache/eix"
# package sets
package_sets = []
sets = emerge *(['--list-sets'])
sets.each_line do |package_set|
package_sets << package_set.to_s.strip
end

if @resource[:name].match(/^@/)
if package_sets.include?(@resource[:name][1..-1].to_s)
return({:name => "#{@resource[:name]}", :ensure => '9999', :version_available => nil, :installed_versions => nil, :installable_versions => "9999,"})
end
end

eix_file = File.directory?('/var/cache/eix') ? '/var/cache/eix/portage.eix' : '/var/cache/eix'
update_eix if !FileUtils.uptodate?(eix_file, %w{/usr/bin/eix /usr/portage/metadata/timestamp})

search_output = nil
Puppet::Util.withenv :LASTVERSION => version_format, :LASTSLOTVERSIONS => slot_versions_format do
search_output = eix *(self.class.eix_search_arguments + ["--exact",search_field,search_value])
Puppet::Util.withenv :EIX_LIMIT => limit, :LASTVERSION => version_format, :LASTSLOTVERSIONS => slot_versions_format, :INSTALLEDVERSIONS => installed_versions_format, :STABLEVERSIONS => installable_versions_format do
search_output = eix *(self.class.eix_search_arguments + ['--exact',search_field,search_value])
end

packages = []
Expand All @@ -127,19 +188,24 @@ def query
result_fields.zip(match.captures) do |field, value|
package[field] = value unless !value or value.empty?
end
if package_slot
package[:version_available] = eix_get_version_for_slot(package[:slot_versions_available], package_slot)
package[:ensure] = eix_get_version_for_slot(package[:installed_slots], package_slot)
# dev-lang python [3.4.5] [3.5.2] [2.7.12:2.7,3.4.5:3.4] [2.7.12:2.7,3.4.5:3.4,3.5.2:3.5] https://www.python.org/ An interpreted, interactive, object-oriented programming language
# prefer checking versions to slots as versions are finer grained
if qatom[:pv]
package[:version_available] = eix_get_version_for_versions(package[:installable_versions_available], qatom[:pv])
package[:ensure] = eix_get_version_for_versions(package[:installed_versions], qatom[:pv])
elsif qatom[:slot]
package[:version_available] = eix_get_version_for_slot(package[:slot_versions_available], qatom[:slot])
package[:ensure] = eix_get_version_for_slot(package[:installed_slots], qatom[:slot])
end

package[:ensure] = package[:ensure] ? package[:ensure] : :absent
packages << package
end
end

case packages.size
when 0
not_found_value = "#{@resource[:category] ? @resource[:category] : "<unspecified category>"}/#{@resource[:name]}"
raise Puppet::Error.new("No package found with the specified name [#{not_found_value}]")
raise Puppet::Error.new("No package found with the specified name [#{@resource[:name]}]")
when 1
return packages[0]
else
Expand All @@ -154,40 +220,74 @@ def latest
self.query[:version_available]
end

private
def eix_get_version_for_versions(versions, target)
# [2.7.10-r1,2.7.12,3.4.3-r1,3.4.5,3.5.2] 3.5.2
return nil if versions.nil?
versions = versions.split(',')
# [2.7.10-r1 2.7.12 3.4.3-r1 3.4.5 3.5.2]
versions.find { |version| version == target }
# 3.5.2
end

private
def eix_get_version_for_slot(versions_and_slots, slot)
# [2.7.12:2.7 3.4.5:3.4 3.5.2:3.5] 3.5
return nil if versions_and_slots.nil?
versions_and_slots = versions_and_slots.split(",")
versions_and_slots.map! { |version_and_slot| version_and_slot.split(":") }
versions_and_slots = versions_and_slots.split(',')
# [2.7.12:2.7 3.4.5:3.4 3.5.2:3.5]
versions_and_slots.map! { |version_and_slot| version_and_slot.split(':') }
# [2.7.12: 2.7
# 3.4.5: 3.4
# 3.5.2: 3.5]
version_for_slot = versions_and_slots.find { |version_and_slot| version_and_slot.last == slot }
# [3.5.2: 3.5]
version_for_slot.first if version_for_slot
end

def self.slot_pattern
/:([\w+.\/*=-]+)$/
# 3.5.2
end

def self.eix_search_format
"'<category> <name> [<installedversions:LASTVERSION>] [<bestversion:LASTVERSION>] [<installedversions:LASTSLOTVERSIONS>] [<bestslotversions:LASTSLOTVERSIONS>] <homepage> <description>\n'"
"'<category> <name> [<installedversions:LASTVERSION>] [<bestversion:LASTVERSION>] [<installedversions:LASTSLOTVERSIONS>] [<installedversions:INSTALLEDVERSIONS>] [<availableversions:STABLEVERSIONS>] [<bestslotversions:LASTSLOTVERSIONS>] <homepage> <description>\n'"
end

def self.eix_result_format
/^(\S+)\s+(\S+)\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+(\S+)\s+(.*)$/
/^(\S+)\s+(\S+)\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+(\S+)\s+(.*)$/
end

def self.eix_result_fields
[:category, :name, :ensure, :version_available, :installed_slots, :slot_versions_available, :vendor, :description]
# ensure:[3.4.5], version_available:[3.5.2], installed_slots:[2.7.12:2.7,3.4.5:3.4], installable_versions:[2.7.10-r1,2.7.12,3.4.3-r1,3.4.5,3.5.2] slot_versions_available:[2.7.12:2.7,3.4.5:3.4,3.5.2:3.5]
[:category, :name, :ensure, :version_available, :installed_slots, :installed_versions, :installable_versions, :slot_versions_available, :vendor, :description]
end

def self.eix_version_format
"{last}<version>{}"
'{last}<version>{}'
end

def self.eix_slot_versions_format
"{!first},{}<version>:<slot>"
'{!first},{}<version>:<slot>'
end

def self.eix_installed_versions_format
'{!first},{}<version>'
end

def self.eix_install_versions_format
'{!first}{!last},{}{}{isstable}<version>{}'
end

def self.eix_limit
'0'
end

def self.eix_search_arguments
["--nocolor", "--pure-packages", "--format",self.eix_search_format]
['--nocolor', '--pure-packages', '--format', self.eix_search_format]
end

def install_options
join_options(@resource[:install_options])
end

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

0 comments on commit 3462fd4

Please sign in to comment.