summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorLamont Granquist <lamont@scriptkiddie.org>2016-05-05 08:31:27 -0700
committerLamont Granquist <lamont@scriptkiddie.org>2016-05-05 08:31:27 -0700
commit88a973df8ad4179d46ff296c80c1a4fb193e2637 (patch)
tree27cb3cd85101ed9628bcbc4ee6d06020538a5d4e /lib
parent4c43a1cc78dce9207fc1461fd1025f29c0c50e98 (diff)
parent318876787b332cff3896ccae5d008702156c47de (diff)
downloadchef-88a973df8ad4179d46ff296c80c1a4fb193e2637.tar.gz
Merge pull request #4888 from chef/lcg/multipackage-apt-provider
Make apt package provider use_multipackage_api aware
Diffstat (limited to 'lib')
-rw-r--r--lib/chef/provider/package.rb2
-rw-r--r--lib/chef/provider/package/apt.rb200
2 files changed, 98 insertions, 104 deletions
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index 6d67cdbbb2..cac0fb6eb0 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -596,7 +596,7 @@ class Chef
# @param args [String] variable number of string arguments
# @return [String] nicely concatenated string or empty string
def a_to_s(*args)
- args.reject { |i| i.nil? || i == "" }.join(" ")
+ args.flatten.reject { |i| i.nil? || i == "" }.join(" ")
end
end
end
diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb
index ac730202b8..8af089e14a 100644
--- a/lib/chef/provider/package/apt.rb
+++ b/lib/chef/provider/package/apt.rb
@@ -17,132 +17,64 @@
#
require "chef/provider/package"
-require "chef/mixin/command"
-require "chef/resource/package"
+require "chef/resource/apt_package"
class Chef
class Provider
class Package
class Apt < Chef::Provider::Package
+ use_multipackage_api
provides :package, platform_family: "debian"
provides :apt_package, os: "linux"
- # return [Hash] mapping of package name to Boolean value
- attr_accessor :is_virtual_package
-
def initialize(new_resource, run_context)
super
- @is_virtual_package = {}
end
def load_current_resource
- @current_resource = Chef::Resource::Package.new(@new_resource.name)
- @current_resource.package_name(@new_resource.package_name)
- check_all_packages_state(@new_resource.package_name)
- @current_resource
+ @current_resource = Chef::Resource::AptPackage.new(new_resource.name)
+ current_resource.package_name(new_resource.package_name)
+ current_resource.version(get_current_versions)
+ current_resource
end
def define_resource_requirements
super
requirements.assert(:all_actions) do |a|
- a.assertion { !@new_resource.source }
+ a.assertion { !new_resource.source }
a.failure_message(Chef::Exceptions::Package, "apt package provider cannot handle source attribute. Use dpkg provider instead")
end
end
- def default_release_options
- # Use apt::Default-Release option only if provider supports it
- "-o APT::Default-Release=#{@new_resource.default_release}" if @new_resource.respond_to?(:default_release) && @new_resource.default_release
+ def package_data
+ @package_data ||= Hash.new do |hash, key|
+ hash[key] = package_data_for(key)
+ end
end
- def check_package_state(pkg)
- is_virtual_package = false
- installed = false
- installed_version = nil
- candidate_version = nil
-
- shell_out_with_timeout!("apt-cache#{expand_options(default_release_options)} policy #{pkg}").stdout.each_line do |line|
- case line
- when /^\s{2}Installed: (.+)$/
- installed_version = $1
- if installed_version == "(none)"
- Chef::Log.debug("#{@new_resource} current version is nil")
- installed_version = nil
- else
- Chef::Log.debug("#{@new_resource} current version is #{installed_version}")
- installed = true
- end
- when /^\s{2}Candidate: (.+)$/
- candidate_version = $1
- if candidate_version == "(none)"
- # This may not be an appropriate assumption, but it shouldn't break anything that already worked -- btm
- is_virtual_package = true
- showpkg = shell_out_with_timeout!("apt-cache showpkg #{pkg}").stdout
- providers = Hash.new
- showpkg.rpartition(/Reverse Provides: ?#{$/}/)[2].each_line do |line|
- provider, version = line.split
- providers[provider] = version
- end
- # Check if the package providing this virtual package is installed
- num_providers = providers.length
- raise Chef::Exceptions::Package, "#{@new_resource.package_name} has no candidate in the apt-cache" if num_providers == 0
- # apt will only install a virtual package if there is a single providing package
- raise Chef::Exceptions::Package, "#{@new_resource.package_name} is a virtual package provided by #{num_providers} packages, you must explicitly select one to install" if num_providers > 1
- # Check if the package providing this virtual package is installed
- Chef::Log.info("#{@new_resource} is a virtual package, actually acting on package[#{providers.keys.first}]")
- ret = check_package_state(providers.keys.first)
- installed = ret[:installed]
- installed_version = ret[:installed_version]
- else
- Chef::Log.debug("#{@new_resource} candidate version is #{$1}")
- end
- end
+ def get_current_versions
+ package_name_array.map do |package_name|
+ package_data[package_name][:current_version]
end
-
- return {
- installed_version: installed_version,
- installed: installed,
- candidate_version: candidate_version,
- is_virtual_package: is_virtual_package,
- }
end
- def check_all_packages_state(package)
- installed_version = {}
- candidate_version = {}
- installed = {}
-
- [package].flatten.each do |pkg|
- ret = check_package_state(pkg)
- is_virtual_package[pkg] = ret[:is_virtual_package]
- installed[pkg] = ret[:installed]
- installed_version[pkg] = ret[:installed_version]
- candidate_version[pkg] = ret[:candidate_version]
+ def get_candidate_versions
+ package_name_array.map do |package_name|
+ package_data[package_name][:candidate_version]
end
+ end
- if package.is_a?(Array)
- @candidate_version = []
- final_installed_version = []
- [package].flatten.each do |pkg|
- @candidate_version << candidate_version[pkg]
- final_installed_version << installed_version[pkg]
- end
- @current_resource.version(final_installed_version)
- else
- @candidate_version = candidate_version[package]
- @current_resource.version(installed_version[package])
- end
+ def candidate_version
+ @candidate_version ||= get_candidate_versions
end
def install_package(name, version)
- name_array = [ name ].flatten
- version_array = [ version ].flatten
- package_name = name_array.zip(version_array).map do |n, v|
- is_virtual_package[n] ? n : "#{n}=#{v}"
- end.join(" ")
- run_noninteractive("apt-get -q -y#{expand_options(default_release_options)}#{expand_options(@new_resource.options)} install #{package_name}")
+ package_name = name.zip(version).map do |n, v|
+ package_data[n][:virtual] ? n : "#{n}=#{v}"
+ end
+ run_noninteractive("apt-get -q -y", default_release_options, new_resource.options, "install", package_name)
end
def upgrade_package(name, version)
@@ -150,24 +82,27 @@ class Chef
end
def remove_package(name, version)
- package_name = [ name ].flatten.join(" ")
- run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} remove #{package_name}")
+ package_name = name.map do |n|
+ package_data[n][:virtual] ? resolve_virtual_package_name(n) : n
+ end
+ run_noninteractive("apt-get -q -y", new_resource.options, "remove", package_name)
end
def purge_package(name, version)
- package_name = [ name ].flatten.join(" ")
- run_noninteractive("apt-get -q -y#{expand_options(@new_resource.options)} purge #{package_name}")
+ package_name = name.map do |n|
+ package_data[n][:virtual] ? resolve_virtual_package_name(n) : n
+ end
+ run_noninteractive("apt-get -q -y", new_resource.options, "purge", package_name)
end
def preseed_package(preseed_file)
- Chef::Log.info("#{@new_resource} pre-seeding package installation instructions")
- run_noninteractive("debconf-set-selections #{preseed_file}")
+ Chef::Log.info("#{new_resource} pre-seeding package installation instructions")
+ run_noninteractive("debconf-set-selections", preseed_file)
end
def reconfig_package(name, version)
- package_name = [ name ].flatten.join(" ")
- Chef::Log.info("#{@new_resource} reconfiguring")
- run_noninteractive("dpkg-reconfigure #{package_name}")
+ Chef::Log.info("#{new_resource} reconfiguring")
+ run_noninteractive("dpkg-reconfigure", name)
end
private
@@ -175,8 +110,67 @@ class Chef
# Runs command via shell_out with magic environment to disable
# interactive prompts. Command is run with default localization rather
# than forcing locale to "C", so command output may not be stable.
- def run_noninteractive(command)
- shell_out_with_timeout!(command, :env => { "DEBIAN_FRONTEND" => "noninteractive", "LC_ALL" => nil })
+ def run_noninteractive(*args)
+ shell_out_with_timeout!(a_to_s(*args), :env => { "DEBIAN_FRONTEND" => "noninteractive" })
+ end
+
+ def default_release_options
+ # Use apt::Default-Release option only if provider supports it
+ "-o APT::Default-Release=#{new_resource.default_release}" if new_resource.respond_to?(:default_release) && new_resource.default_release
+ end
+
+ def resolve_package_versions(pkg)
+ current_version = nil
+ candidate_version = nil
+ run_noninteractive("apt-cache", default_release_options, "policy", pkg).stdout.each_line do |line|
+ case line
+ when /^\s{2}Installed: (.+)$/
+ current_version = ( $1 != "(none)" ) ? $1 : nil
+ Chef::Log.debug("#{new_resource} installed version for #{pkg} is #{$1}")
+ when /^\s{2}Candidate: (.+)$/
+ candidate_version = ( $1 != "(none)" ) ? $1 : nil
+ Chef::Log.debug("#{new_resource} candidate version for #{pkg} is #{$1}")
+ end
+ end
+ [ current_version, candidate_version ]
+ end
+
+ def resolve_virtual_package_name(pkg)
+ showpkg = run_noninteractive("apt-cache showpkg", pkg).stdout
+ partitions = showpkg.rpartition(/Reverse Provides: ?#{$/}/)
+ return nil if partitions[0] == "" && partitions[1] == "" # not found in output
+ set = partitions[2].lines.each_with_object(Set.new) do |line, acc|
+ # there may be multiple reverse provides for a single package
+ acc.add(line.split[0])
+ end
+ if set.size > 1
+ raise Chef::Exceptions::Package, "#{new_resource.package_name} is a virtual package provided by multiple packages, you must explicitly select one"
+ end
+ return set.to_a.first
+ end
+
+ def package_data_for(pkg)
+ virtual = false
+ current_version = nil
+ candidate_version = nil
+
+ current_version, candidate_version = resolve_package_versions(pkg)
+
+ if candidate_version.nil?
+ newpkg = resolve_virtual_package_name(pkg)
+
+ if newpkg
+ virtual = true
+ Chef::Log.info("#{new_resource} is a virtual package, actually acting on package[#{newpkg}]")
+ current_version, candidate_version = resolve_package_versions(newpkg)
+ end
+ end
+
+ return {
+ current_version: current_version,
+ candidate_version: candidate_version,
+ virtual: virtual,
+ }
end
end