diff options
-rw-r--r-- | lib/chef/provider/package.rb | 3 | ||||
-rw-r--r-- | lib/chef/provider/package/zypper.rb | 163 | ||||
-rw-r--r-- | spec/functional/resource/zypper_package_spec.rb | 11 | ||||
-rw-r--r-- | spec/unit/provider/package/zypper_spec.rb | 25 | ||||
-rw-r--r-- | spec/unit/provider/package_spec.rb | 4 |
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 |