summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Dibowitz <phil@ipom.com>2021-02-08 10:51:20 -0800
committerGitHub <noreply@github.com>2021-02-08 10:51:20 -0800
commit7a4fd1824fa0c6f03962d85ec5a87ded12f9794f (patch)
tree706093efbbe88e2aa9afbf986ed6e63ef5c916ae
parent63f877629afe6c3060a6af411531fe962ce1fdb3 (diff)
downloadchef-7a4fd1824fa0c6f03962d85ec5a87ded12f9794f.tar.gz
Fix downgrades in apt_package (#10993)
`apt_package` never even looks at `new_resource.version` when deciding what version to install. That's silly. This fixes that. It requires the version be exact (as the `yum_package` did for most of Chef's history), and if you specify a non-existent version, it'll raise an exception. The unit tests for this provider are a bit of a mess, so I kinda stuck in the tests where I could. ``` [2021-02-07T15:59:35-08:00] TRACE: apt_package[hub] installed version for hub is 2.14.2-1vcrs.1 [2021-02-07T15:59:35-08:00] TRACE: apt_package[hub] candidate version for hub is 2.14.2-1vcrs.1 [2021-02-07T15:59:35-08:00] TRACE: apt_package[hub] hub 2.14.2-1vcrs.1 needs updating to 2.7.0~ds1-1vcrs.1 [2021-02-07T15:59:38-08:00] INFO: apt_package[hub] installed hub at 2.7.0~ds1-1vcrs.1 ``` Signed-off-by: Phil Dibowitz <phil@ipom.com>
-rw-r--r--lib/chef/provider/package/apt.rb28
-rw-r--r--spec/functional/resource/apt_package_spec.rb2
-rw-r--r--spec/unit/provider/package/apt_spec.rb98
3 files changed, 110 insertions, 18 deletions
diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb
index dbacaedb07..6f2f645ead 100644
--- a/lib/chef/provider/package/apt.rb
+++ b/lib/chef/provider/package/apt.rb
@@ -176,6 +176,7 @@ class Chef
def resolve_package_versions(pkg)
current_version = nil
candidate_version = nil
+ all_versions = []
run_noninteractive("apt-cache", default_release_options, "policy", pkg).stdout.each_line do |line|
case line
when /^\s{2}Installed: (.+)$/
@@ -184,9 +185,34 @@ class Chef
when /^\s{2}Candidate: (.+)$/
candidate_version = ( $1 != "(none)" ) ? $1 : nil
logger.trace("#{new_resource} candidate version for #{pkg} is #{$1}")
+ when /\s+(?:\*\*\* )?(\S+) \d+/
+ all_versions << $1
end
end
- [ current_version, candidate_version ]
+ # This is a bit ugly... really this whole provider needs
+ # to be rewritten to use target_version_array and friends, but
+ # for now this gets us moving
+ idx = package_name_array.index(pkg)
+ chosen_version =
+ if idx
+ user_ver = new_version_array[idx]
+ if user_ver
+ if all_versions.include?(user_ver)
+ user_ver
+ else
+ logger.debug("User specified a version that's not available")
+ nil
+ end
+ else
+ # user didn't specify a version, use candidate
+ candidate_version
+ end
+ else
+ # this probably means we're redirected from a virtual
+ # package, so... just go with candidate version
+ candidate_version
+ end
+ [ current_version, chosen_version ]
end
def resolve_virtual_package_name(pkg)
diff --git a/spec/functional/resource/apt_package_spec.rb b/spec/functional/resource/apt_package_spec.rb
index 9f10e27731..8e888ce54a 100644
--- a/spec/functional/resource/apt_package_spec.rb
+++ b/spec/functional/resource/apt_package_spec.rb
@@ -186,7 +186,7 @@ describe Chef::Resource::AptPackage, metadata do
it "raises a reasonable error for action :install" do
expect do
package_resource.run_action(:install)
- end.to raise_error(Mixlib::ShellOut::ShellCommandFailed)
+ end.to raise_error(Chef::Exceptions::Package)
end
end
diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb
index 2ff1f0fddc..45d6d16229 100644
--- a/spec/unit/provider/package/apt_spec.rb
+++ b/spec/unit/provider/package/apt_spec.rb
@@ -48,6 +48,21 @@ describe Chef::Provider::Package::Apt do
@timeout = 900
end
+ def ubuntu1804downgrade_stubs
+ so = instance_double(Mixlib::ShellOut, stdout: "apt 1.6~beta1 (amd64)\notherstuff\n")
+ so2 = instance_double(Mixlib::ShellOut, error?: false)
+ allow(@provider).to receive(:shell_out).with("apt-get --version").and_return(so)
+ allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.6~beta1", "gt", "1.1.0").and_return(so2)
+ end
+
+ def ubuntu1404downgrade_stubs
+ so = instance_double(Mixlib::ShellOut, stdout: "apt 1.0.1ubuntu2 for amd64 compiled on Dec 8 2016 16:23:38\notherstuff\n")
+ so2 = instance_double(Mixlib::ShellOut, error?: true)
+ allow(@provider).to receive(:shell_out).with("apt-get --version").and_return(so)
+ allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.0.1ubuntu2", "gt", "1.1.0").and_return(so2)
+ allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.0.1ubuntu2", "eq", "1.1.0").and_return(so2)
+ end
+
describe "when loading current resource" do
it "should create a current resource with the name of the new_resource" do
@@ -237,6 +252,72 @@ describe Chef::Provider::Package::Apt do
).and_return(@shell_out)
expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
end
+
+ it "downgrades when requested" do
+ ubuntu1804downgrade_stubs
+ so = instance_double(Mixlib::ShellOut, stdout: "apt 1.6~beta1 (amd64)\notherstuff\n")
+ so2 = instance_double(Mixlib::ShellOut, error?: false)
+ allow(@provider).to receive(:shell_out).with("apt-get --version").and_return(so)
+ allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.6~beta1", "gt", "1.1.0").and_return(so2)
+
+ @new_resource.package_name("libmysqlclient-dev")
+ @new_resource.version("5.1.41-3ubuntu12.7")
+ real_package_out = <<~RPKG_STDOUT
+ libmysqlclient-dev:
+ Installed: 5.1.41-3ubuntu12.10
+ Candidate: 5.1.41-3ubuntu12.10
+ Version table:
+ *** 5.1.41-3ubuntu12.10 0
+ 500 http://us.archive.ubuntu.com/ubuntu/ lucid-updates/main packages
+ 100 /var/lib/dpkg/status
+ 5.1.41-3ubuntu12.7 0
+ 500 http://security.ubuntu.com/ubuntu/ lucid-security/main packages
+ 5.1.41-3ubuntu12 0
+ 500 http://us.archive.ubuntu.com/ubuntu/ lucid/main packages
+ RPKG_STDOUT
+ real_package = double(stdout: real_package_out, exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-cache", "policy", "libmysqlclient-dev",
+ env: { "DEBIAN_FRONTEND" => "noninteractive" },
+ timeout: @timeout
+ ).and_return(real_package)
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-get", "-q", "-y", "--allow-downgrades", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold", "install", "libmysqlclient-dev=5.1.41-3ubuntu12.7",
+ env: { "DEBIAN_FRONTEND" => "noninteractive" },
+ timeout: @timeout
+ )
+ @provider.run_action(:install)
+ end
+
+ it "raises an exception if bad version specified" do
+ @new_resource.package_name("libmysqlclient-dev")
+ @new_resource.version("non_existent")
+ real_package_out = <<~RPKG_STDOUT
+ libmysqlclient-dev:
+ Installed: 5.1.41-3ubuntu12.10
+ Candidate: 5.1.41-3ubuntu12.10
+ Version table:
+ *** 5.1.41-3ubuntu12.10 0
+ 500 http://us.archive.ubuntu.com/ubuntu/ lucid-updates/main packages
+ 100 /var/lib/dpkg/status
+ 5.1.41-3ubuntu12.7 0
+ 500 http://security.ubuntu.com/ubuntu/ lucid-security/main packages
+ 5.1.41-3ubuntu12 0
+ 500 http://us.archive.ubuntu.com/ubuntu/ lucid/main packages
+ RPKG_STDOUT
+ real_package = double(stdout: real_package_out, exitstatus: 0)
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-cache", "policy", @new_resource.package_name,
+ env: { "DEBIAN_FRONTEND" => "noninteractive" } ,
+ timeout: @timeout
+ ).and_return(real_package)
+ expect(@provider).to receive(:shell_out_compacted!).with(
+ "apt-cache", "showpkg", @new_resource.package_name,
+ env: { "DEBIAN_FRONTEND" => "noninteractive" } ,
+ timeout: @timeout
+ ).and_return(real_package)
+ expect { @provider.run_action(:install) }.to raise_error(Chef::Exceptions::Package)
+ end
end
context "after loading the current resource" do
@@ -257,21 +338,6 @@ describe Chef::Provider::Package::Apt do
})
end
- def ubuntu1804downgrade_stubs
- so = instance_double(Mixlib::ShellOut, stdout: "apt 1.6~beta1 (amd64)\notherstuff\n")
- so2 = instance_double(Mixlib::ShellOut, error?: false)
- allow(@provider).to receive(:shell_out).with("apt-get --version").and_return(so)
- allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.6~beta1", "gt", "1.1.0").and_return(so2)
- end
-
- def ubuntu1404downgrade_stubs
- so = instance_double(Mixlib::ShellOut, stdout: "apt 1.0.1ubuntu2 for amd64 compiled on Dec 8 2016 16:23:38\notherstuff\n")
- so2 = instance_double(Mixlib::ShellOut, error?: true)
- allow(@provider).to receive(:shell_out).with("apt-get --version").and_return(so)
- allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.0.1ubuntu2", "gt", "1.1.0").and_return(so2)
- allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.0.1ubuntu2", "eq", "1.1.0").and_return(so2)
- end
-
describe "install_package" do
before(:each) do
ubuntu1804downgrade_stubs
@@ -590,7 +656,7 @@ describe Chef::Provider::Package::Apt do
end
end
- describe "#action_install" do
+ describe "#action_upgrade" do
it "should run dpkg to compare versions if an existing version is installed" do
allow(@provider).to receive(:get_current_versions).and_return("1.4.0")
allow(@new_resource).to receive(:allow_downgrade).and_return(false)