summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Cowie <jcowie@etsy.com>2015-02-13 12:12:13 +0000
committerJon Cowie <jcowie@etsy.com>2015-02-13 12:12:13 +0000
commit3386dff3694a229993eb11c21baa493fa09a025f (patch)
treeeb2cbae4cbe229b8b3dbe7bff1172f2891629cfa
parent49156a559ecdd88c2d7d183c7ace4d77d185a384 (diff)
downloadchef-3386dff3694a229993eb11c21baa493fa09a025f.tar.gz
Updated version of #2125 to fix CHEF-2911
This pull request allows a version requirement such as ` = 1.0.1` to be specified in either the resource name or the version attribute of a yum_package resource, and modifies the yum package provider to update existing packages based on that requirement. Also adds specs to test this new functionality.
-rw-r--r--CHANGELOG.md2
-rw-r--r--DOC_CHANGES.md6
-rw-r--r--lib/chef/provider/package/yum.rb39
-rw-r--r--spec/unit/provider/package/yum_spec.rb136
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