summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Smith <tsmith@chef.io>2020-11-11 17:41:12 -0800
committerGitHub <noreply@github.com>2020-11-11 17:41:12 -0800
commitea6d2b9f92d57d00673d22930007c9d5e8c9e754 (patch)
tree5c25f8baaeb3230ab4d006f87b3f5a8dc4e4a3fb
parenta734171a38e3381814572715966dd772caf19c0e (diff)
parent7360fd6b7f188440d417948f592e0865b8ebe6b6 (diff)
downloadchef-ea6d2b9f92d57d00673d22930007c9d5e8c9e754.tar.gz
Merge pull request #10631 from chef/lcg/zypper-fix
Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r--lib/chef/provider/package.rb3
-rw-r--r--lib/chef/provider/package/zypper.rb163
-rw-r--r--spec/functional/resource/zypper_package_spec.rb11
-rw-r--r--spec/unit/provider/package/zypper_spec.rb25
-rw-r--r--spec/unit/provider/package_spec.rb4
5 files changed, 108 insertions, 98 deletions
diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb
index 8ace5e033b..6cbc8c7b24 100644
--- a/lib/chef/provider/package.rb
+++ b/lib/chef/provider/package.rb
@@ -443,6 +443,9 @@ class Chef
elsif current_version && !allow_downgrade && version_compare(current_version, new_version) == 1
logger.warn("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{new_version}. Skipping...)")
target_version_array.push(nil)
+ elsif version_equals?(current_version, candidate_version)
+ logger.trace("#{new_resource} #{package_name} #{candidate_version} is already installed")
+ target_version_array.push(nil)
else
logger.trace("#{new_resource} #{package_name} #{current_version} needs updating to #{new_version}")
target_version_array.push(new_version)
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index da6bf0efbf..6ed7339e32 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -26,87 +26,18 @@ class Chef
class Package
class Zypper < Chef::Provider::Package
use_multipackage_api
+ allow_nils
provides :package, platform_family: "suse"
provides :zypper_package
- def get_versions(package_name)
- candidate_version = current_version = nil
- is_installed = false
- logger.trace("#{new_resource} checking zypper")
- status = shell_out!("zypper", "--non-interactive", "info", package_name)
- status.stdout.each_line do |line|
- case line
- when /^Version *: (.+) *$/
- candidate_version = $1.strip
- logger.trace("#{new_resource} version #{candidate_version}")
- when /^Installed *: Yes.*$/ # http://rubular.com/r/9StcAMjOn6
- is_installed = true
- logger.trace("#{new_resource} is installed")
- when /^Status *: out-of-date \(version (.+) installed\) *$/
- current_version = $1.strip
- logger.trace("#{new_resource} out of date version #{current_version}")
- end
- end
- current_version ||= candidate_version if is_installed
- { current_version: current_version, candidate_version: candidate_version }
- end
-
- def versions
- @versions ||=
- begin
- raw_versions = package_name_array.map do |package_name|
- get_versions(package_name)
- end
- Hash[*package_name_array.zip(raw_versions).flatten]
- end
- end
-
- def get_candidate_versions
- package_name_array.map do |package_name|
- versions[package_name][:candidate_version]
- end
- end
-
- def get_current_versions
- package_name_array.map do |package_name|
- versions[package_name][:current_version]
- end
- end
-
- def packages_all_locked?(names, versions)
- names.all? { |n| locked_packages.include? n }
- end
-
- def packages_all_unlocked?(names, versions)
- names.all? { |n| !locked_packages.include? n }
- end
-
- def locked_packages
- @locked_packages ||=
- begin
- locked = shell_out!("zypper", "locks")
- locked.stdout.each_line.map do |line|
- line.split("|").shift(2).last.strip
- end
- end
- end
-
def load_current_resource
@current_resource = Chef::Resource::ZypperPackage.new(new_resource.name)
current_resource.package_name(new_resource.package_name)
-
- @candidate_version = get_candidate_versions
current_resource.version(get_current_versions)
-
current_resource
end
- def zypper_version
- @zypper_version ||=
- `zypper -V 2>&1`.scan(/\d+/).join(".").to_f
- end
-
def install_package(name, version)
zypper_package("install", global_options, *options, "--auto-agree-with-licenses", allow_downgrade, name, version)
end
@@ -134,10 +65,71 @@ class Chef
private
+ def get_current_versions
+ package_name_array.each_with_index.map { |pkg, i| installed_version(i) }
+ end
+
+ def candidate_version
+ @candidate_version ||= package_name_array.each_with_index.map { |pkg, i| available_version(i) }
+ end
+
+ def resolve_current_version(package_name)
+ latest_version = current_version = nil
+ is_installed = false
+ logger.trace("#{new_resource} checking zypper")
+ status = shell_out!("zypper", "--non-interactive", "info", package_name)
+ status.stdout.each_line do |line|
+ case line
+ when /^Version *: (.+) *$/
+ latest_version = $1.strip
+ logger.trace("#{new_resource} version #{latest_version}")
+ when /^Installed *: Yes.*$/ # http://rubular.com/r/9StcAMjOn6
+ is_installed = true
+ logger.trace("#{new_resource} is installed")
+ when /^Status *: out-of-date \(version (.+) installed\) *$/
+ current_version = $1.strip
+ logger.trace("#{new_resource} out of date version #{current_version}")
+ end
+ end
+ current_version ||= latest_version if is_installed
+ current_version
+ end
+
+ def resolve_available_version(package_name, new_version)
+ search_string = new_version.nil? ? package_name : "#{package_name}=#{new_version}"
+ so = shell_out!("zypper", "--non-interactive", "search", "-s", "--provides", "--match-exact", "--type=package", search_string)
+ so.stdout.each_line do |line|
+ if md = line.match(/^(\S*)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(\S+)\s+\|\s+(.*)$/)
+ (status, name, type, version, arch, repo) = [ md[1], md[2], md[3], md[4], md[5], md[6] ]
+ next if version == "Version" # header
+
+ return version
+ end
+ end
+ nil
+ end
+
+ def available_version(index)
+ @available_version ||= []
+ @available_version[index] ||= resolve_available_version(package_name_array[index], safe_version_array[index])
+ @available_version[index]
+ end
+
+ def installed_version(index)
+ @installed_version ||= []
+ @installed_version[index] ||= resolve_current_version(package_name_array[index])
+ @installed_version[index]
+ end
+
def zip(names, versions)
names.zip(versions).map do |n, v|
(v.nil? || v.empty?) ? n : "#{n}=#{v}"
- end
+ end.compact
+ end
+
+ def zypper_version
+ @zypper_version ||=
+ `zypper -V 2>&1`.scan(/\d+/).join(".").to_f
end
def zypper_package(command, global_options, *options, names, versions)
@@ -160,6 +152,35 @@ class Chef
def global_options
new_resource.global_options
end
+
+ def packages_all_locked?(names, versions)
+ names.all? { |n| locked_packages.include? n }
+ end
+
+ def packages_all_unlocked?(names, versions)
+ names.all? { |n| !locked_packages.include? n }
+ end
+
+ def locked_packages
+ @locked_packages ||=
+ begin
+ locked = shell_out!("zypper", "locks")
+ locked.stdout.each_line.map do |line|
+ line.split("|").shift(2).last.strip
+ end
+ end
+ end
+
+ def safe_version_array
+ if new_resource.version.is_a?(Array)
+ new_resource.version
+ elsif new_resource.version.nil?
+ package_name_array.map { nil }
+ else
+ [ new_resource.version ]
+ end
+ end
+
end
end
end
diff --git a/spec/functional/resource/zypper_package_spec.rb b/spec/functional/resource/zypper_package_spec.rb
index e56684b6f8..ce6a3bf33c 100644
--- a/spec/functional/resource/zypper_package_spec.rb
+++ b/spec/functional/resource/zypper_package_spec.rb
@@ -96,6 +96,17 @@ describe Chef::Resource::ZypperPackage, :requires_root, :suse_only do
expect(zypper_package.updated_by_last_action?).to be false
expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.2-1.#{pkg_arch}$")
end
+
+ it "multipackage installs which result in nils from the superclass" do
+ # this looks weird, it tests an internal condition of the allow_nils behavior where the arrays passed to install_package will have
+ # nil values, and ensures that doesn't wind up creating weirdness in the resulting shell_out that causes it to fail
+ preinstall("chef_rpm-1.2-1.#{pkg_arch}.rpm")
+ zypper_package.package_name(%w{chef_rpm chef_rpm})
+ zypper_package.version(["1.2", "1.10"])
+ zypper_package.run_action(:install)
+ expect(zypper_package.updated_by_last_action?).to be true
+ expect(shell_out("rpm -q --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}\n' chef_rpm").stdout.chomp).to match("^chef_rpm-1.10-1.#{pkg_arch}$")
+ end
end
context "with versions" do
diff --git a/spec/unit/provider/package/zypper_spec.rb b/spec/unit/provider/package/zypper_spec.rb
index fe1cd25ee3..a1f6629f55 100644
--- a/spec/unit/provider/package/zypper_spec.rb
+++ b/spec/unit/provider/package/zypper_spec.rb
@@ -96,31 +96,6 @@ describe Chef::Provider::Package::Zypper do
provider.load_current_resource
end
- it "should set the candidate version if zypper info has one (zypper version < 1.13.0)" do
- status = double(stdout: "Version: 1.0\nInstalled: No\nStatus: out-of-date (version 0.9 installed)", exitstatus: 0)
-
- allow(provider).to receive(:shell_out_compacted!).and_return(status)
- provider.load_current_resource
- expect(provider.candidate_version).to eql(["1.0"])
- end
-
- it "should set the candidate version if zypper info has one (zypper version >= 1.13.0)" do
- status = double(stdout: "Version : 1.0 \nInstalled : No \nStatus : out-of-date (version 0.9 installed)", exitstatus: 0)
-
- allow(provider).to receive(:shell_out_compacted!).and_return(status)
- provider.load_current_resource
- expect(provider.candidate_version).to eql(["1.0"])
- end
-
- it "should have differing current and candidate versions if zypper detects an upgrade" do
- status = double(stdout: "Version : 1.0 \nInstalled : Yes \nStatus : out-of-date (version 0.9 installed)", exitstatus: 0)
-
- allow(provider).to receive(:shell_out_compacted!).and_return(status)
- provider.load_current_resource
- expect(provider.get_current_versions).to eq(["0.9"])
- expect(provider.get_candidate_versions).to eq(["1.0"])
- end
-
it "should return the current resouce" do
expect(provider.load_current_resource).to eql(current_resource)
end
diff --git a/spec/unit/provider/package_spec.rb b/spec/unit/provider/package_spec.rb
index 505dfff417..a6e1da188c 100644
--- a/spec/unit/provider/package_spec.rb
+++ b/spec/unit/provider/package_spec.rb
@@ -517,8 +517,8 @@ describe "Chef::Provider::Package - Multi" do
end
it "installs the specified version when some are out of date" do
- current_resource.version(["1.0", "6.2"])
- new_resource.version(["1.0", "6.3"])
+ current_resource.version(["1.0", "6.1"])
+ new_resource.version(["1.0", "6.2"])
provider.run_action(:install)
expect(new_resource).to be_updated
end