| diff --git a/lib/puppet/provider/package/portage.rb b/lib/puppet/provider/package/portage.rb |
| index 374667c..12160c6 100644 |
| --- a/lib/puppet/provider/package/portage.rb |
| +++ b/lib/puppet/provider/package/portage.rb |
| @@ -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 => '/' |
| @@ -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 = [] |
| @@ -57,65 +65,123 @@ 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) |
| + 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 |
| + |
| + def uninstall |
| + should = @resource.should(:ensure) |
| + cmd = %w{--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 |
| - emerge name |
| end |
| |
| - # The common package name format. |
| - def package_name |
| - @resource[:category] ? "#{@resource[:category]}/#{@resource[:name]}" : @resource[:name] |
| + def reinstall |
| + self.install |
| end |
| |
| - def package_name_without_slot |
| - package_name.sub(self.class.slot_pattern, '') |
| + def update |
| + self.install |
| end |
| |
| - def package_slot |
| - if match = package_name.match(self.class.slot_pattern) |
| - match[1] |
| + def qatom |
| + output_format = self.qatom_output_format |
| + result_format = self.qatom_result_format |
| + result_fields = self.qatom_result_fields |
| + @atom ||= 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 |
| + @atom = package_info |
| + rescue Puppet::ExecutionFailure => detail |
| + raise Puppet::Error.new(detail) |
| end |
| end |
| |
| - def package_atom_with_version(version) |
| - if slot = package_slot |
| - "=#{package_name_without_slot}-#{version}:#{package_slot}" |
| - else |
| - "=#{package_name}-#{version}" |
| - end |
| + def qatom_output_format |
| + '"[%{CATEGORY}] [%{PN}] [%{PV}] [%[PR]] [%[SLOT]] [%[pfx]] [%[sfx]]"' |
| end |
| |
| - def uninstall |
| - emerge "--unmerge", package_name |
| + def qatom_result_format |
| + /^\"\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\]\s+\[(\S*)\](.*)\"$/ |
| end |
| |
| - def reinstall |
| - self.install |
| + def qatom_result_fields |
| + [:category, :pn, :pv, :pr, :slot, :pfx, :sfx] |
| end |
| |
| - def update |
| - self.install |
| + def self.get_sets |
| + @sets ||= begin |
| + @sets = emerge *(['--list-sets']) |
| + end |
| 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] |
| + |
| + @eix_result ||= begin |
| + # package sets |
| + package_sets = [] |
| + self.class.get_sets.each_line do |package_set| |
| + package_sets << package_set.to_s.strip |
| + end |
| |
| - begin |
| - eix_file = File.directory?("/var/cache/eix") ? "/var/cache/eix/portage.eix" : "/var/cache/eix" |
| + 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 = [] |
| @@ -127,10 +193,19 @@ 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 |
| + # version_available is what we CAN install / update to |
| + # ensure is what is currently installed |
| + # This DOES NOT choose to install/upgrade or not, just provides current info |
| + # prefer checking versions to slots as versions are finer grained |
| + if qatom[:pv] |
| + package[:version_available] = eix_get_version_for_versions(package[:installable_versions], 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 |
| @@ -138,10 +213,9 @@ def query |
| |
| 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] |
| + @eix_result = packages[0] |
| else |
| raise Puppet::Error.new("More than one package with the specified name [#{search_value}], please use the category parameter to disambiguate") |
| end |
| @@ -155,39 +229,73 @@ def latest |
| 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(":") } |
| - version_for_slot = versions_and_slots.find { |version_and_slot| version_and_slot.last == slot } |
| + 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[1..-1] } |
| + # [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 |