diff --git a/Gemfile.lock b/Gemfile.lock index 3aa1f196730..2a09eaaf725 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,9 @@ GIT remote: https://github.com/chef/ohai.git - revision: c63a8dca74db3fd542a082c7b6626ff497904290 + revision: d5d03240e6ee1130ef38441c1d25a7ca08df0cab branch: main specs: - ohai (18.1.4) + ohai (18.1.11) chef-config (>= 14.12, < 19) chef-utils (>= 16.0, < 19) ffi (~> 1.9) @@ -222,8 +222,8 @@ GEM faraday-net_http (3.0.2) fauxhai-ng (9.3.0) net-ssh - ffi (1.15.5) - ffi (1.15.5-x64-mingw-ucrt) + ffi (1.16.2) + ffi (1.16.2-x64-mingw-ucrt) ffi-libarchive (1.1.3) ffi (~> 1.0) ffi-win32-extensions (1.0.4) @@ -427,7 +427,7 @@ GEM uuidtools (2.2.0) vault (0.17.0) aws-sigv4 - webmock (3.18.1) + webmock (3.19.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb index 6f2f645ead6..aaf6e67145a 100644 --- a/lib/chef/provider/package/apt.rb +++ b/lib/chef/provider/package/apt.rb @@ -124,6 +124,11 @@ def unlock_package(name, version) private + # @return [String] package name with or without anchors attached to it. + def resolve_package(pkg) + new_resource.anchor_package_regex ? "^#{pkg}$" : pkg + end + # @return [String] version of apt-get which is installed def apt_version @apt_version ||= shell_out("apt-get --version").stdout.match(/^apt (\S+)/)[1] @@ -174,10 +179,12 @@ def config_file_options end def resolve_package_versions(pkg) + # apt-cache considers package names as regex by default. The anchor_package_regex flag will decide whether to match name exact string or not + pkg_name = resolve_package(pkg) current_version = nil candidate_version = nil all_versions = [] - run_noninteractive("apt-cache", default_release_options, "policy", pkg).stdout.each_line do |line| + run_noninteractive("apt-cache", default_release_options, "policy", pkg_name).stdout.each_line do |line| case line when /^\s{2}Installed: (.+)$/ current_version = ( $1 != "(none)" ) ? $1 : nil @@ -216,7 +223,9 @@ def resolve_package_versions(pkg) end def resolve_virtual_package_name(pkg) - showpkg = run_noninteractive("apt-cache", "showpkg", pkg).stdout + # apt-cache considers package names as regex by default. The anchor_package_regex flag will decide whether to match name exact string or not + pkg_name = resolve_package(pkg) + showpkg = run_noninteractive("apt-cache", "showpkg", pkg_name).stdout partitions = showpkg.rpartition(/Reverse Provides: ?#{$/}/) return nil if partitions[0] == "" && partitions[1] == "" # not found in output diff --git a/lib/chef/resource/apt_package.rb b/lib/chef/resource/apt_package.rb index 85c2ad4faf3..c86e05bef44 100644 --- a/lib/chef/resource/apt_package.rb +++ b/lib/chef/resource/apt_package.rb @@ -52,6 +52,21 @@ class AptPackage < Chef::Resource::Package options '--no-install-recommends' end ``` + + **Prevent the apt_package resource from installing packages with pattern matching names**: + + By default, the apt_package resource will install the named package. + If it can't find a package with the exact same name, it will treat the package name as regex string and match with any package that matches that regex. + This may lead Chef Infra Client to install one or more packages with names that match that regex. + + In this example, `anchor_package_regex true` prevents the apt_package resource from installing matching packages if it can't find the `lua5.3` package. + + ```ruby + apt_package 'lua5.3' do + version '5.3.3-1.1ubuntu2' + anchor_package_regex true + end + ``` DOC description "Use the **apt_package** resource to manage packages on Debian, Ubuntu, and other platforms that use the APT package system." @@ -75,6 +90,10 @@ class AptPackage < Chef::Resource::Package description: "A Hash of response file variables in the form of {'VARIABLE' => 'VALUE'}.", default: {}, desired_state: false + property :anchor_package_regex, [TrueClass, FalseClass], + introduced: "18.3", + description: "A Boolean flag that allows (`false`) or prevents (`true`) apt_package from matching the named package with packages by regular expression if it can't find a package with the exact same name.", + default: false end end end diff --git a/omnibus/Gemfile.lock b/omnibus/Gemfile.lock index e3cb5ad19f2..4913cc3b0f5 100644 --- a/omnibus/Gemfile.lock +++ b/omnibus/Gemfile.lock @@ -1,23 +1,23 @@ GIT remote: https://github.com/chef/omnibus-software.git - revision: f06bbea744458f2cb63c98e6891f9cfb904a1f90 + revision: c44dea9a73e9cc12097e32edbc8d3f965b7d7474 branch: main specs: - omnibus-software (23.5.289) + omnibus-software (23.9.296) omnibus (>= 9.0.0) GIT remote: https://github.com/chef/omnibus.git - revision: 9c0643a3a44f3e7119789c2093bfc8edd78c74ff + revision: 82dae896a066542f1b66dc866186f856f37e518e branch: main specs: - omnibus (9.0.21) + omnibus (9.0.23) aws-sdk-s3 (~> 1.116.0) chef-cleanroom (~> 1.0) chef-utils (>= 15.4) contracts (>= 0.16.0, < 0.17.0) ffi-yajl (~> 2.2) - license_scout (~> 1.3) + license_scout (~> 1.0) mixlib-shellout (>= 2.0, < 4.0) mixlib-versioning ohai (>= 16, < 19) @@ -53,7 +53,7 @@ GEM aws-sigv4 (1.5.2) aws-eventstream (~> 1, >= 1.0.2) bcrypt_pbkdf (1.1.0) - berkshelf (8.0.5) + berkshelf (8.0.9) chef (>= 15.7.32) chef-config cleanroom (~> 1.0) @@ -192,6 +192,9 @@ GEM faraday (>= 1, < 3) faraday-net_http (3.0.2) ffi (1.15.5) + ffi (1.15.5-x64-mingw-ucrt) + ffi (1.15.5-x64-mingw32) + ffi (1.15.5-x86-mingw32) ffi-libarchive (1.1.3) ffi (~> 1.0) ffi-win32-extensions (1.0.4) @@ -453,7 +456,7 @@ GEM vault (0.17.0) aws-sigv4 webrick (1.8.1) - win32-api (1.10.1-universal-mingw32) + win32-api (1.10.1) win32-certstore (0.6.15) chef-powershell (>= 1.0.12) ffi diff --git a/spec/unit/provider/package/apt_spec.rb b/spec/unit/provider/package/apt_spec.rb index cf50c124a94..5efcfe74870 100644 --- a/spec/unit/provider/package/apt_spec.rb +++ b/spec/unit/provider/package/apt_spec.rb @@ -63,11 +63,16 @@ def ubuntu1404downgrade_stubs allow(@provider).to receive(:shell_out).with("dpkg", "--compare-versions", "1.0.1ubuntu2", "eq", "1.1.0").and_return(so2) end + def get_pkg_name_str(name) + # Package name with or without anchors attached to it based on the flag. + @new_resource.anchor_package_regex ? "^#{name}$" : name + end + describe "when loading current resource" do it "should create a current resource with the name of the new_resource" do expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "policy", @new_resource.package_name, + "apt-cache", "policy", get_pkg_name_str(@new_resource.package_name), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(@shell_out) @@ -107,7 +112,7 @@ def ubuntu1404downgrade_stubs POLICY_STDOUT policy = double(stdout: policy_out, exitstatus: 0) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "policy", "conic-smarms", + "apt-cache", "policy", get_pkg_name_str("conic-smarms"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(policy) @@ -116,7 +121,7 @@ def ubuntu1404downgrade_stubs SHOWPKG_STDOUT showpkg = double(stdout: showpkg_out, exitstatus: 0) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "showpkg", "conic-smarms", + "apt-cache", "showpkg", get_pkg_name_str("conic-smarms"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(showpkg) @@ -135,7 +140,7 @@ def ubuntu1404downgrade_stubs VPKG_STDOUT virtual_package = double(stdout: virtual_package_out, exitstatus: 0) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "policy", "libmysqlclient15-dev", + "apt-cache", "policy", get_pkg_name_str("libmysqlclient15-dev"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(virtual_package) @@ -159,7 +164,7 @@ def ubuntu1404downgrade_stubs SHOWPKG_STDOUT showpkg = double(stdout: showpkg_out, exitstatus: 0) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "showpkg", "libmysqlclient15-dev", + "apt-cache", "showpkg", get_pkg_name_str("libmysqlclient15-dev"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(showpkg) @@ -178,7 +183,7 @@ def ubuntu1404downgrade_stubs RPKG_STDOUT real_package = double(stdout: real_package_out, exitstatus: 0) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "policy", "libmysqlclient-dev", + "apt-cache", "policy", get_pkg_name_str("libmysqlclient-dev"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(real_package) @@ -195,7 +200,7 @@ def ubuntu1404downgrade_stubs VPKG_STDOUT virtual_package = double(stdout: virtual_package_out, exitstatus: 0) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "policy", "mp3-decoder", + "apt-cache", "policy", get_pkg_name_str("mp3-decoder"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(virtual_package) @@ -222,7 +227,7 @@ def ubuntu1404downgrade_stubs SHOWPKG_STDOUT showpkg = double(stdout: showpkg_out, exitstatus: 0) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "showpkg", "mp3-decoder", + "apt-cache", "showpkg", get_pkg_name_str("mp3-decoder"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(showpkg) @@ -236,7 +241,7 @@ def ubuntu1404downgrade_stubs @new_resource.default_release("lenny-backports") @new_resource.provider(nil) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "-o", "APT::Default-Release=lenny-backports", "policy", "irssi", + "apt-cache", "-o", "APT::Default-Release=lenny-backports", "policy", get_pkg_name_str("irssi"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(@shell_out) @@ -246,7 +251,7 @@ def ubuntu1404downgrade_stubs it "raises an exception if a source is specified (CHEF-5113)" do @new_resource.source "pluto" expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "policy", @new_resource.package_name, + "apt-cache", "policy", get_pkg_name_str(@new_resource.package_name), env: { "DEBIAN_FRONTEND" => "noninteractive" } , timeout: @timeout ).and_return(@shell_out) @@ -277,7 +282,7 @@ def ubuntu1404downgrade_stubs RPKG_STDOUT real_package = double(stdout: real_package_out, exitstatus: 0) expect(@provider).to receive(:shell_out_compacted!).with( - "apt-cache", "policy", "libmysqlclient-dev", + "apt-cache", "policy", get_pkg_name_str("libmysqlclient-dev"), env: { "DEBIAN_FRONTEND" => "noninteractive" }, timeout: @timeout ).and_return(real_package) @@ -307,12 +312,12 @@ def ubuntu1404downgrade_stubs 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, + "apt-cache", "policy", get_pkg_name_str(@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, + "apt-cache", "showpkg", get_pkg_name_str(@new_resource.package_name), env: { "DEBIAN_FRONTEND" => "noninteractive" } , timeout: @timeout ).and_return(real_package)