diff options
Diffstat (limited to 'lib/chef/provider/package/yum.rb')
-rw-r--r-- | lib/chef/provider/package/yum.rb | 168 |
1 files changed, 117 insertions, 51 deletions
diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb index 505f5fd6a3..405e4177ab 100644 --- a/lib/chef/provider/package/yum.rb +++ b/lib/chef/provider/package/yum.rb @@ -18,7 +18,7 @@ require 'chef/config' require 'chef/provider/package' -require 'chef/mixin/command' +require 'chef/mixin/command' # handle_command_failures require 'chef/mixin/shell_out' require 'chef/resource/package' require 'singleton' @@ -976,14 +976,6 @@ class Chef end end - def allow_downgrade - if @new_resource.respond_to?("allow_downgrade") - @new_resource.allow_downgrade - else - false - end - end - # Helpers # @@ -1018,7 +1010,7 @@ class Chef if status.exitstatus > 0 command_output = "STDOUT: #{stdout}" command_output << "STDERR: #{stderr}" - handle_command_failures(status, command_output, {}) + Chef::Mixin::Command.handle_command_failures(status, command_output, {}) end end @@ -1054,9 +1046,20 @@ class Chef # 3) or a dependency, eg: "foo >= 1.1" # Check if we have name or name+arch which has a priority over a dependency - unless @yum.package_available?(@new_resource.package_name) - # If they aren't in the installed packages they could be a dependency - parse_dependency + package_name_array.each_with_index do |n, index| + unless @yum.package_available?(n) + # If they aren't in the installed packages they could be a dependency + dep = parse_dependency(n, new_version_array[index]) + if dep + if @new_resource.package_name.is_a?(Array) + @new_resource.package_name(package_name_array - [n] + [dep.first]) + @new_resource.version(new_version_array - [new_version_array[index]] + [dep.last]) if dep.last + else + @new_resource.package_name(dep.first) + @new_resource.version(dep.last) if dep.last + end + end + end end # Don't overwrite an existing arch @@ -1090,10 +1093,18 @@ class Chef Chef::Log.debug("#{@new_resource} checking yum info for #{new_resource}") - installed_version = @yum.installed_version(@new_resource.package_name, arch) - @current_resource.version(installed_version) - - @candidate_version = @yum.candidate_version(@new_resource.package_name, arch) + installed_version = [] + @candidate_version = [] + package_name_array.each do |pkg| + installed_version << @yum.installed_version(pkg, arch) + @candidate_version << @yum.candidate_version(pkg, arch) + end + if installed_version.size == 1 + @current_resource.version(installed_version[0]) + @candidate_version = @candidate_version[0] + else + @current_resource.version(installed_version) + end Chef::Log.debug("#{@new_resource} installed version: #{installed_version || "(none)"} candidate version: " + "#{@candidate_version || "(none)"}") @@ -1101,43 +1112,77 @@ class Chef @current_resource end - def install_package(name, version) - if @new_resource.source - yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}") - else - # Work around yum not exiting with an error if a package doesn't exist for CHEF-2062 - if @yum.version_available?(name, version, arch) + def install_remote_package(name, version) + # Work around yum not exiting with an error if a package doesn't exist + # for CHEF-2062 + all_avail = as_array(name).zip(as_array(version)).any? do |n, v| + @yum.version_available?(n, v, arch) + end + method = log_method = nil + methods = [] + if all_avail + # More Yum fun: + # + # yum install of an old name+version will exit(1) + # yum install of an old name+version+arch will exit(0) for some reason + # + # Some packages can be installed multiple times like the kernel + as_array(name).zip(as_array(version)).each do |n, v| method = "install" log_method = "installing" - - # More Yum fun: - # - # yum install of an old name+version will exit(1) - # yum install of an old name+version+arch will exit(0) for some reason - # - # Some packages can be installed multiple times like the kernel - unless @yum.allow_multi_install.include?(name) - if RPMVersion.parse(@current_resource.version) > RPMVersion.parse(version) - # Unless they want this... + idx = package_name_array.index(n) + unless @yum.allow_multi_install.include?(n) + if RPMVersion.parse(current_version_array[idx]) > RPMVersion.parse(v) + # We allow downgrading only in the evenit of single-package + # rules where the user explicitly allowed it if allow_downgrade method = "downgrade" log_method = "downgrading" else # we bail like yum when the package is older - raise Chef::Exceptions::Package, "Installed package #{name}-#{@current_resource.version} is newer " + - "than candidate package #{name}-#{version}" + raise Chef::Exceptions::Package, "Installed package #{n}-#{current_version_array[idx]} is newer " + + "than candidate package #{n}-#{v}" end end end + # methods don't count for packages we won't be touching + next if RPMVersion.parse(current_version_array[idx]) == RPMVersion.parse(v) + methods << method + end - repo = @yum.package_repository(name, version, arch) - Chef::Log.info("#{@new_resource} #{log_method} #{name}-#{version}#{yum_arch} from #{repo} repository") + # We could split this up into two commands if we wanted to, but + # for now, just don't support this. + if methods.uniq.length > 1 + raise Chef::Exceptions::Package, "Multipackage rule #{name} has a mix of upgrade and downgrade packages. Cannot proceed." + end - yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{name}-#{version}#{yum_arch}") - else - raise Chef::Exceptions::Package, "Version #{version} of #{name} not found. Did you specify both version " + - "and release? (version-release, e.g. 1.84-10.fc6)" + repos = [] + pkg_string_bits = [] + index = 0 + as_array(name).zip(as_array(version)).each do |n, v| + s = '' + unless v == current_version_array[index] + s = "#{n}-#{v}#{yum_arch}" + repo = @yum.package_repository(n, v, arch) + repos << "#{s} from #{repo} repository" + pkg_string_bits << s + end + index += 1 end + pkg_string = pkg_string_bits.join(' ') + Chef::Log.info("#{@new_resource} #{log_method} #{repos.join(' ')}") + yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} #{method} #{pkg_string}") + else + raise Chef::Exceptions::Package, "Version #{version} of #{name} not found. Did you specify both version " + + "and release? (version-release, e.g. 1.84-10.fc6)" + end + end + + def install_package(name, version) + if @new_resource.source + yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} localinstall #{@new_resource.source}") + else + install_remote_package(name, version) end if flush_cache[:after] @@ -1156,10 +1201,11 @@ class Chef # Hacky - better overall solution? Custom compare in Package provider? def action_upgrade # Could be uninstalled or have no candidate - if @current_resource.version.nil? || candidate_version.nil? + if @current_resource.version.nil? || !candidate_version_array.any? super - # Ensure the candidate is newer - elsif RPMVersion.parse(candidate_version) > RPMVersion.parse(@current_resource.version) + elsif candidate_version_array.zip(current_version_array).any? do |c, i| + RPMVersion.parse(c) > RPMVersion.parse(i) + end super else Chef::Log.debug("#{@new_resource} is at the latest version - nothing to do") @@ -1172,10 +1218,13 @@ class Chef def remove_package(name, version) if version - yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{name}-#{version}#{yum_arch}") + remove_str = as_array(name).zip(as_array(version)).map do |x| + "#{x.join('-')}#{yum_arch}" + end.join(' ') else - yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{name}#{yum_arch}") + remove_str = as_array(name).map { |n| "#{n}#{yum_arch}" }.join(' ') end + yum_command("yum -d0 -e0 -y#{expand_options(@new_resource.options)} remove #{remove_str}") if flush_cache[:after] @yum.reload @@ -1218,9 +1267,19 @@ class Chef # matching them up with an actual package so the standard resource handling can apply. # # There is currently no support for filename matching. - def parse_dependency + def parse_dependency(name,version) # Transform the package_name into a requirement - yum_require = RPMRequire.parse(@new_resource.package_name) + + # If we are passed a version or a version constraint we have to assume it's a requirement first. If it can't be + # parsed only yum_require.name will be set and @new_resource.version will be left intact + if version + require_string = "#{name} #{version}" + else + # Transform the package_name into a requirement, might contain a version, could just be + # a match for virtual provides + require_string = name + end + yum_require = RPMRequire.parse(require_string) # and gather all the packages that have a Provides feature satisfying the requirement. # It could be multiple be we can only manage one packages = @yum.packages_from_require(yum_require) @@ -1238,8 +1297,11 @@ class Chef unless packages.empty? new_package_name = packages.first.name - Chef::Log.debug("#{@new_resource} no package found for #{@new_resource.package_name} " + - "but matched Provides for #{new_package_name}") + new_package_version = packages.first.version.to_s + debug_msg = "#{name}: Unable to match package '#{name}' but matched #{packages.size} " + debug_msg << packages.size == 1 ? "package" : "packages" + debug_msg << ", selected '#{new_package_name}' version '#{new_package_version}'" + Chef::Log.debug(debug_msg) # Ensure it's not the same package under a different architecture unique_names = [] @@ -1254,7 +1316,11 @@ class Chef "specific version.") end - @new_resource.package_name(new_package_name) + if yum_require.version.to_s.nil? + new_package_version = nil + end + + [new_package_name,new_package_version] end end |