diff options
-rw-r--r-- | CHANGELOG.md | 2 | ||||
-rw-r--r-- | DOC_CHANGES.md | 6 | ||||
-rw-r--r-- | lib/chef/provider/package/yum.rb | 39 | ||||
-rw-r--r-- | spec/unit/provider/package/yum_spec.rb | 136 |
4 files changed, 156 insertions, 27 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 9970243e2b..43ce6d11b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,8 @@ Remove comments of a service being enabled/disabled in FreeBSD. [Fixes #1791](https://github.com/chef/chef/issues/1791) * [**Will Albenzi**](https://github.com/walbenzi): CHEF-4591: Knife commands to manipulate env_run_list on nodes +* [**Jon Cowie**](https://github.com/jonlives): + CHEF-2911: Fix yum_package provider to respect version requirements in package name and version attribute ### Chef Contributions * ruby 1.9.3 support is dropped diff --git a/DOC_CHANGES.md b/DOC_CHANGES.md index e1b90d304d..e85e546ee9 100644 --- a/DOC_CHANGES.md +++ b/DOC_CHANGES.md @@ -52,3 +52,9 @@ will stop chef_gem from automatically installing at compile_time. False is the recommended setting as long as the gem is only used in provider code (a best practice) and not used directly in recipe code. +## Yum Package provider now supports version requirements + +A documented feature of the yum_package provider was the ability to specify a version requirement such as ` = 1.0.1.el5` in the resource name. +However, this did not actually work. It has now been fixed, and additionally version requirements are now supported in the `version` attribute +of yum_package as well. + diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb index 3c4721b6a1..05fb76c4f5 100644 --- a/lib/chef/provider/package/yum.rb +++ b/lib/chef/provider/package/yum.rb @@ -1046,15 +1046,17 @@ 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 - package_name_array.each do |n| + 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) + 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]) + @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) + @new_resource.package_name(dep.first) + @new_resource.version(dep.last) if dep.last end end end @@ -1265,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(name) + def parse_dependency(name,version) # Transform the package_name into a requirement - yum_require = RPMRequire.parse(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) @@ -1285,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 = [] @@ -1301,11 +1316,15 @@ class Chef "specific version.") end - new_package_name + if yum_require.version.to_s.nil? + new_package_version = nil + end + + [new_package_name,new_package_version] end end end end end -end +end
\ No newline at end of file diff --git a/spec/unit/provider/package/yum_spec.rb b/spec/unit/provider/package/yum_spec.rb index e5e32bc9c0..aecbd1c34e 100644 --- a/spec/unit/provider/package/yum_spec.rb +++ b/spec/unit/provider/package/yum_spec.rb @@ -231,26 +231,102 @@ describe Chef::Provider::Package::Yum do @provider.load_current_resource end - it "should search provides if package name can't be found then set package_name to match" do + context "when the package name isn't found" do + let(:yum_cache) { double( + 'Chef::Provider::Yum::YumCache', + :reload_installed => true, + :reset => true, + :installed_version => "1.0.1.el5", + :candidate_version => "2.0.1.el5", + :package_available? => false, + :version_available? => true, + :disable_extra_repo_control => true + ) + } + + before do + allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(yum_cache) + @pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", []) + expect(yum_cache).to receive(:packages_from_require).and_return([@pkg]) + end + + it "should search provides then set package_name to match" do + @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) + @provider.load_current_resource + expect(@new_resource.package_name).to eq('test-package') + expect(@new_resource.version).to eq(nil) + end + + it "should search provides then set version to match if a requirement was passed in the package name" do + @new_resource = Chef::Resource::YumPackage.new('test-package = 2.0.1.el5') + @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) + @provider.load_current_resource + expect(@new_resource.package_name).to eq('test-package') + expect(@new_resource.version).to eq('2.0.1.el5') + end + + + it "should search provides then set version to match if a requirement was passed in the version" do + @new_resource = Chef::Resource::YumPackage.new('test-package') + @new_resource.version('= 2.0.1.el5') + @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) + @provider.load_current_resource + expect(@new_resource.package_name).to eq('test-package') + expect(@new_resource.version).to eq('2.0.1.el5') + end + + + it "should search provides and not set the version to match if a specific version was requested" do + @new_resource = Chef::Resource::YumPackage.new('test-package') + @new_resource.version('3.0.1.el5') + @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) + @provider.load_current_resource + expect(@new_resource.package_name).to eq('test-package') + expect(@new_resource.version).to eq('3.0.1.el5') + end + + it "should search provides then set versions to match if requirements were passed in the package name as an array" do + @new_resource = Chef::Resource::YumPackage.new(['test-package = 2.0.1.el5']) + @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) + @provider.load_current_resource + expect(@new_resource.package_name).to eq(['test-package']) + expect(@new_resource.version).to eq(['2.0.1.el5']) + end + + it "should search provides and not set the versions to match if specific versions were requested in an array" do + @new_resource = Chef::Resource::YumPackage.new(['test-package']) + @new_resource.version(['3.0.1.el5']) + @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) + @provider.load_current_resource + expect(@new_resource.package_name).to eq(['test-package']) + expect(@new_resource.version).to eq(['3.0.1.el5']) + end + + end + + it "should not return an error if no version number is specified in the resource" do + @new_resource = Chef::Resource::YumPackage.new('test-package') @yum_cache = double( - 'Chef::Provider::Yum::YumCache', - :reload_installed => true, - :reset => true, - :installed_version => "1.2.4-11.18.el5", - :candidate_version => "1.2.4-11.18.el5", - :package_available? => false, - :version_available? => true, - :disable_extra_repo_control => true + 'Chef::Provider::Yum::YumCache', + :reload_installed => true, + :reset => true, + :installed_version => "1.0.1.el5", + :candidate_version => "2.0.1.el5", + :package_available? => false, + :version_available? => true, + :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) - pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "1.2.4-11.18.el5", "x86_64", []) + pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", []) expect(@yum_cache).to receive(:packages_from_require).and_return([pkg]) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource expect(@new_resource.package_name).to eq("test-package") + expect(@new_resource.version).to eq(nil) end - it "should search provides if package name can't be found, warn about multiple matches, but use the first one" do + it "should give precedence to the version attribute when both a requirement in the resource name and a version attribute are specified" do + @new_resource = Chef::Resource::YumPackage.new('test-package') @yum_cache = double( 'Chef::Provider::Yum::YumCache', :reload_installed => true, @@ -262,13 +338,38 @@ describe Chef::Provider::Package::Yum do :disable_extra_repo_control => true ) allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) - pkg_x = Chef::Provider::Package::Yum::RPMPackage.new("test-package-x", "1.2.4-11.18.el5", "x86_64", []) - pkg_y = Chef::Provider::Package::Yum::RPMPackage.new("test-package-y", "1.2.6-11.3.el5", "i386", []) - expect(@yum_cache).to receive(:packages_from_require).and_return([pkg_x, pkg_y]) - expect(Chef::Log).to receive(:warn).exactly(1).times.with(%r{matched multiple Provides}) + pkg = Chef::Provider::Package::Yum::RPMPackage.new("test-package", "2.0.1.el5", "x86_64", []) + expect(@yum_cache).to receive(:packages_from_require).and_return([pkg]) + @new_resource = Chef::Resource::YumPackage.new('test-package = 2.0.1.el5') + @new_resource.version('3.0.1.el5') + @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) + @provider.load_current_resource + expect(@new_resource.package_name).to eq('test-package') + expect(@new_resource.version).to eq('3.0.1.el5') + end + + it "should correctly detect the installed states of an array of package names and version numbers" do + @yum_cache = double( + 'Chef::Provider::Yum::YumCache', + :reload_installed => true, + :reset => true, + :installed_version => "1.0.1.el5", + :candidate_version => "2.0.1.el5", + :package_available? => false, + :version_available? => true, + :disable_extra_repo_control => true + ) + allow(Chef::Provider::Package::Yum::YumCache).to receive(:instance).and_return(@yum_cache) + + expect(@yum_cache).to receive(:packages_from_require).exactly(4).times.and_return([]) + expect(@yum_cache).to receive(:reload_provides).twice + + @new_resource = Chef::Resource::YumPackage.new(['test-package','test-package2']) + @new_resource.version(['2.0.1.el5','3.0.1.el5']) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource - expect(@new_resource.package_name).to eq("test-package-x") + expect(@new_resource.package_name).to eq(['test-package','test-package2']) + expect(@new_resource.version).to eq(['2.0.1.el5','3.0.1.el5']) end it "should search provides if no package is available - if no match in installed provides then load the complete set" do @@ -287,6 +388,7 @@ describe Chef::Provider::Package::Yum do expect(@yum_cache).to receive(:reload_provides) @provider = Chef::Provider::Package::Yum.new(@new_resource, @run_context) @provider.load_current_resource + expect(@new_resource.version).to eq(nil) end it "should search provides if no package is available and not load the complete set if action is :remove or :purge" do @@ -1959,4 +2061,4 @@ describe "Chef::Provider::Package::Yum - Multi" do @provider.install_package(["cups", "vim"], ["1.2.4-11.19.el5", '1.0']) end end -end +end
\ No newline at end of file |