diff options
-rw-r--r-- | CHANGELOG.md | 42 | ||||
-rw-r--r-- | Dockerfile | 2 | ||||
-rw-r--r-- | Gemfile.lock | 10 | ||||
-rw-r--r-- | VERSION | 2 | ||||
-rw-r--r-- | chef-config/lib/chef-config/version.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/package.rb | 39 | ||||
-rw-r--r-- | lib/chef/provider/package/apt.rb | 7 | ||||
-rw-r--r-- | lib/chef/provider/package/chocolatey.rb | 11 | ||||
-rw-r--r-- | lib/chef/provider/package/dnf.rb | 4 | ||||
-rw-r--r-- | lib/chef/provider/package/dnf/dnf_helper.py | 10 | ||||
-rw-r--r-- | lib/chef/provider/package/dnf/python_helper.rb | 15 | ||||
-rw-r--r-- | lib/chef/provider/package/rpm.rb | 5 | ||||
-rw-r--r-- | lib/chef/provider/package/windows.rb | 15 | ||||
-rw-r--r-- | lib/chef/provider/package/yum.rb | 24 | ||||
-rw-r--r-- | lib/chef/provider/zypper_repository.rb | 112 | ||||
-rw-r--r-- | lib/chef/resource/zypper_repository.rb | 1 | ||||
-rw-r--r-- | lib/chef/version.rb | 2 | ||||
-rw-r--r-- | spec/unit/provider/package/rubygems_spec.rb | 5 | ||||
-rw-r--r-- | spec/unit/provider/zypper_repository_spec.rb | 124 | ||||
-rw-r--r-- | spec/unit/resource/zypper_repository_spec.rb | 20 |
20 files changed, 380 insertions, 72 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f0e6f4c5e..6d20d63ebd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,34 +1,40 @@ <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ --> -<!-- latest_release 13.5.6 --> -## [v13.5.6](https://github.com/chef/chef/tree/v13.5.6) (2017-09-29) +<!-- latest_release 13.5.8 --> +## [v13.5.8](https://github.com/chef/chef/tree/v13.5.8) (2017-10-03) #### Merged Pull Requests -- Bump InSpec to v1.40.0 [#6460](https://github.com/chef/chef/pull/6460) ([adamleff](https://github.com/adamleff)) +- Fixes to package upgrade behaviour [#6428](https://github.com/chef/chef/pull/6428) ([jonlives](https://github.com/jonlives)) <!-- latest_release --> -<!-- release_rollup since=12.21.14 --> -### Changes since 12.21.14 release +<!-- release_rollup since=13.5.3 --> +### Changes since 13.5.3 release #### Merged Pull Requests -- Bump InSpec to v1.40.0 [#6460](https://github.com/chef/chef/pull/6460) ([adamleff](https://github.com/adamleff)) <!-- 13.5.6 --> -- Force encoding to UTF_8 in chef-shell to prevent failures [#6447](https://github.com/chef/chef/pull/6447) ([tas50](https://github.com/tas50)) <!-- 13.5.5 --> +- Fixes to package upgrade behaviour [#6428](https://github.com/chef/chef/pull/6428) ([jonlives](https://github.com/jonlives)) <!-- 13.5.8 --> +- Import the zypper GPG key before templating the repo [#6410](https://github.com/chef/chef/pull/6410) ([tas50](https://github.com/tas50)) <!-- 13.5.7 --> - only warn about skipping sync once [#6454](https://github.com/chef/chef/pull/6454) ([Happycoil](https://github.com/Happycoil)) <!-- 13.5.4 --> -- Replace which apt-get check with simple debian check in apt resources [#6409](https://github.com/chef/chef/pull/6409) ([tas50](https://github.com/tas50)) <!-- 13.4.25 --> -- Quiet the output of the zypper refresh and add force [#6408](https://github.com/chef/chef/pull/6408) ([tas50](https://github.com/tas50)) <!-- 13.4.26 --> -- Remove unused requires in yum_repository [#6413](https://github.com/chef/chef/pull/6413) ([tas50](https://github.com/tas50)) <!-- 13.4.27 --> -- Open apt resources up to prevent breaking change [#6417](https://github.com/chef/chef/pull/6417) ([tas50](https://github.com/tas50)) <!-- 13.4.28 --> -- Don't catch SIGCHLD from dnf_helper.py [#6416](https://github.com/chef/chef/pull/6416) ([nemith](https://github.com/nemith)) <!-- 13.4.29 --> -- Add throttle and metalink options to yum_repository [#6431](https://github.com/chef/chef/pull/6431) ([tas50](https://github.com/tas50)) <!-- 13.4.31 --> -- Fix Knife search ID only option to actually filter result set [#6438](https://github.com/chef/chef/pull/6438) ([dimsh99](https://github.com/dimsh99)) <!-- 13.4.32 --> -- Update dependencies to pull in InSpec v1.39.1 [#6440](https://github.com/chef/chef/pull/6440) ([adamleff](https://github.com/adamleff)) <!-- 13.4.34 --> -- Only accept MM/DD/YYYY for windows_task start_day [#6434](https://github.com/chef/chef/pull/6434) ([jaym](https://github.com/jaym)) <!-- 13.4.35 --> -- fix password property is sensitive for mount resource [#6442](https://github.com/chef/chef/pull/6442) ([dimsh99](https://github.com/dimsh99)) <!-- 13.4.36 --> +- Force encoding to UTF_8 in chef-shell to prevent failures [#6447](https://github.com/chef/chef/pull/6447) ([tas50](https://github.com/tas50)) <!-- 13.5.5 --> +- Bump InSpec to v1.40.0 [#6460](https://github.com/chef/chef/pull/6460) ([adamleff](https://github.com/adamleff)) <!-- 13.5.6 --> <!-- release_rollup --> <!-- latest_stable_release --> -## [v12.21.14](https://github.com/chef/chef/tree/v12.21.14) (2017-09-27) +## [v13.5.3](https://github.com/chef/chef/tree/v13.5.3) (2017-10-03) + +#### Merged Pull Requests +- fix password property is sensitive for mount resource [#6442](https://github.com/chef/chef/pull/6442) ([dimsh99](https://github.com/dimsh99)) +- Only accept MM/DD/YYYY for windows_task start_day [#6434](https://github.com/chef/chef/pull/6434) ([jaym](https://github.com/jaym)) +- Update dependencies to pull in InSpec v1.39.1 [#6440](https://github.com/chef/chef/pull/6440) ([adamleff](https://github.com/adamleff)) +- Fix Knife search ID only option to actually filter result set [#6438](https://github.com/chef/chef/pull/6438) ([dimsh99](https://github.com/dimsh99)) +- Add throttle and metalink options to yum_repository [#6431](https://github.com/chef/chef/pull/6431) ([tas50](https://github.com/tas50)) +- Don't catch SIGCHLD from dnf_helper.py [#6416](https://github.com/chef/chef/pull/6416) ([nemith](https://github.com/nemith)) +- Open apt resources up to prevent breaking change [#6417](https://github.com/chef/chef/pull/6417) ([tas50](https://github.com/tas50)) +- Remove unused requires in yum_repository [#6413](https://github.com/chef/chef/pull/6413) ([tas50](https://github.com/tas50)) +- Quiet the output of the zypper refresh and add force [#6408](https://github.com/chef/chef/pull/6408) ([tas50](https://github.com/tas50)) +- Replace which apt-get check with simple debian check in apt resources [#6409](https://github.com/chef/chef/pull/6409) ([tas50](https://github.com/tas50)) <!-- latest_stable_release --> +## [v12.21.14](https://github.com/chef/chef/tree/v12.21.14) (2017-09-27) + ## [v13.4.24](https://github.com/chef/chef/tree/v13.4.24) (2017-09-14) #### Merged Pull Requests diff --git a/Dockerfile b/Dockerfile index 51e1669a26..25e6b5ff0b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ FROM busybox MAINTAINER Chef Software, Inc. <docker@chef.io> ARG CHANNEL=stable -ARG VERSION=12.21.14 +ARG VERSION=13.5.3 RUN wget "http://packages.chef.io/files/${CHANNEL}/chef/${VERSION}/el/5/chef-${VERSION}-1.el5.x86_64.rpm" -O /tmp/chef-client.rpm && \ rpm2cpio /tmp/chef-client.rpm | cpio -idmv && \ diff --git a/Gemfile.lock b/Gemfile.lock index ee88c25abc..6b60b069d9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,10 +17,10 @@ GIT PATH remote: . specs: - chef (13.5.6) + chef (13.5.8) addressable bundler (>= 1.10) - chef-config (= 13.5.6) + chef-config (= 13.5.8) chef-zero (>= 13.0) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -47,10 +47,10 @@ PATH specinfra (~> 2.10) syslog-logger (~> 1.6) uuidtools (~> 2.1.5) - chef (13.5.6-universal-mingw32) + chef (13.5.8-universal-mingw32) addressable bundler (>= 1.10) - chef-config (= 13.5.6) + chef-config (= 13.5.8) chef-zero (>= 13.0) diff-lcs (~> 1.2, >= 1.2.4) erubis (~> 2.7) @@ -92,7 +92,7 @@ PATH PATH remote: chef-config specs: - chef-config (13.5.6) + chef-config (13.5.8) addressable fuzzyurl mixlib-config (~> 2.0) @@ -1 +1 @@ -13.5.6
\ No newline at end of file +13.5.8
\ No newline at end of file diff --git a/chef-config/lib/chef-config/version.rb b/chef-config/lib/chef-config/version.rb index f144a36c63..b5e461f962 100644 --- a/chef-config/lib/chef-config/version.rb +++ b/chef-config/lib/chef-config/version.rb @@ -21,7 +21,7 @@ module ChefConfig CHEFCONFIG_ROOT = File.expand_path("../..", __FILE__) - VERSION = "13.5.6" + VERSION = "13.5.8" end # diff --git a/lib/chef/provider/package.rb b/lib/chef/provider/package.rb index 4810728524..95d16dd666 100644 --- a/lib/chef/provider/package.rb +++ b/lib/chef/provider/package.rb @@ -323,10 +323,38 @@ class Chef # # Note that most likely we need a spaceship operator on versions that subclasses can implement # and we should have `version_compare(v1, v2)` that returns `v1 <=> v2`. + + # This method performs a strict equality check between two strings representing version numbers # + # This function will eventually be deprecated in favour of the below version_equals function. + def target_version_already_installed?(current_version, target_version) - return false unless current_version && target_version - current_version == target_version + version_equals?(current_version, target_version) + end + + # Note that most likely we need a spaceship operator on versions that subclasses can implement + # and we should have `version_compare(v1, v2)` that returns `v1 <=> v2`. + + # This method performs a strict equality check between two strings representing version numbers + # + def version_equals?(v1, v2) + return false unless v1 && v2 + v1 == v2 + end + + # This function compares two version numbers and returns 'spaceship operator' style results, ie: + # if v1 < v2 then return -1 + # if v1 = v2 then return 0 + # if v1 > v2 then return 1 + # if v1 and v2 are not comparable then return nil + # + # By default, this function will use Gem::Version comparison. Subclasses can reimplement this method + # for package-management system specific versions. + def version_compare(v1, v2) + gem_v1 = Gem::Version.new(v1) + gem_v2 = Gem::Version.new(v2) + + gem_v1 <=> gem_v2 end # Check the current_version against the new_resource.version, possibly using fuzzy @@ -439,16 +467,19 @@ class Chef each_package do |package_name, new_version, current_version, candidate_version| case action when :upgrade - if target_version_already_installed?(current_version, new_version) + if version_equals?(current_version, new_version) # this is an odd use case Chef::Log.debug("#{new_resource} #{package_name} #{new_version} is already installed -- you are equality pinning with an :upgrade action, this may be deprecated in the future") target_version_array.push(nil) - elsif target_version_already_installed?(current_version, candidate_version) + elsif version_equals?(current_version, candidate_version) Chef::Log.debug("#{new_resource} #{package_name} #{candidate_version} is already installed") target_version_array.push(nil) elsif candidate_version.nil? Chef::Log.debug("#{new_resource} #{package_name} has no candidate_version to upgrade to") target_version_array.push(nil) + elsif version_compare(current_version, candidate_version) == 1 && !new_resource.allow_downgrade + Chef::Log.debug("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{candidate_version}. Skipping...)") + target_version_array.push(nil) else Chef::Log.debug("#{new_resource} #{package_name} is out of date, will upgrade to #{candidate_version}") target_version_array.push(candidate_version) diff --git a/lib/chef/provider/package/apt.rb b/lib/chef/provider/package/apt.rb index 3f8c34f50c..da86016621 100644 --- a/lib/chef/provider/package/apt.rb +++ b/lib/chef/provider/package/apt.rb @@ -127,6 +127,13 @@ class Chef private + def version_compare(v1, v2) + gem_v1 = v1.gsub(/[_+]/, "+" => "-", "_" => "-") unless v1.nil? + gem_v2 = v2.gsub(/[_+]/, "+" => "-", "_" => "-") unless v2.nil? + + Gem::Version.new(gem_v1) <=> Gem::Version.new(gem_v2) + end + # Runs command via shell_out with magic environment to disable # interactive prompts. Command is run with default localization rather # than forcing locale to "C", so command output may not be stable. diff --git a/lib/chef/provider/package/chocolatey.rb b/lib/chef/provider/package/chocolatey.rb index a1ec4504a0..c832116377 100644 --- a/lib/chef/provider/package/chocolatey.rb +++ b/lib/chef/provider/package/chocolatey.rb @@ -141,6 +141,17 @@ EOS private + def version_compare(v1, v2) + if v1 == "latest" || v2 == "latest" + return 0 + end + + gem_v1 = Gem::Version.new(v1) + gem_v2 = Gem::Version.new(v2) + + gem_v1 <=> gem_v2 + end + # Magic to find where chocolatey is installed in the system, and to # return the full path of choco.exe # diff --git a/lib/chef/provider/package/dnf.rb b/lib/chef/provider/package/dnf.rb index 31279e8312..a602a9b768 100644 --- a/lib/chef/provider/package/dnf.rb +++ b/lib/chef/provider/package/dnf.rb @@ -126,6 +126,10 @@ class Chef end end + def version_compare(v1, v2) + python_helper.compare_versions(v1, v2) + end + # @returns Array<Version> def available_version(index) @available_version ||= [] diff --git a/lib/chef/provider/package/dnf/dnf_helper.py b/lib/chef/provider/package/dnf/dnf_helper.py index eb4d238f65..501d6fceee 100644 --- a/lib/chef/provider/package/dnf/dnf_helper.py +++ b/lib/chef/provider/package/dnf/dnf_helper.py @@ -26,6 +26,14 @@ def flushcache(): pass get_sack().load_system_repo(build_cache=True) +def versioncompare(versions): + sack = get_sack() + if (versions[0] is None) or (versions[1] is None): + sys.stdout.write('0\n') + else: + evr_comparison = sack.evr_cmp(versions[0], versions[1]) + sys.stdout.write('{}\n'.format(evr_comparison)) + def query(command): sack = get_sack() @@ -86,5 +94,7 @@ while 1: query(command) elif command['action'] == "flushcache": flushcache() + elif command['action'] == "versioncompare": + versioncompare(command['versions']) else: raise RuntimeError("bad command") diff --git a/lib/chef/provider/package/dnf/python_helper.rb b/lib/chef/provider/package/dnf/python_helper.rb index 04f0298861..1801caa1c1 100644 --- a/lib/chef/provider/package/dnf/python_helper.rb +++ b/lib/chef/provider/package/dnf/python_helper.rb @@ -61,6 +61,15 @@ class Chef start if stdin.nil? end + def compare_versions(version1, version2) + with_helper do + json = build_version_query("versioncompare", [version1, version2]) + Chef::Log.debug "sending '#{json}' to python helper" + stdin.syswrite json + "\n" + stdout.sysread(4096).chomp.to_i + end + end + # @returns Array<Version> def query(action, provides, version = nil, arch = nil) with_helper do @@ -109,6 +118,12 @@ class Chef FFI_Yajl::Encoder.encode(hash) end + def build_version_query(action, versions) + hash = { "action" => action } + hash["versions"] = versions + FFI_Yajl::Encoder.encode(hash) + end + def parse_response(output) array = output.split.map { |x| x == "nil" ? nil : x } array.each_slice(3).map { |x| Version.new(*x) }.first diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb index 7ec24f8c24..07617c814e 100644 --- a/lib/chef/provider/package/rpm.rb +++ b/lib/chef/provider/package/rpm.rb @@ -18,6 +18,7 @@ require "chef/provider/package" require "chef/resource/package" require "chef/mixin/get_source_from_package" +require "chef/provider/package/yum/rpm_utils" class Chef class Provider @@ -109,6 +110,10 @@ class Chef private + def version_compare(v1, v2) + Chef::Provider::Package::Yum::RPMVersion.parse(v1) <=> Chef::Provider::Package::Yum::RPMVersion.parse(v2) + end + def uri_scheme?(str) scheme = URI.split(str).first return false unless scheme diff --git a/lib/chef/provider/package/windows.rb b/lib/chef/provider/package/windows.rb index ca9d1e813a..bade7f27a3 100644 --- a/lib/chef/provider/package/windows.rb +++ b/lib/chef/provider/package/windows.rb @@ -165,6 +165,10 @@ class Chef # # @return [Boolean] true if new_version is equal to or included in current_version def target_version_already_installed?(current_version, new_version) + version_equals?(current_version, new_version) + end + + def version_equals?(current_version, new_version) Chef::Log.debug("Checking if #{new_resource} version '#{new_version}' is already installed. #{current_version} is currently installed") if current_version.is_a?(Array) current_version.include?(new_version) @@ -179,6 +183,17 @@ class Chef private + def version_compare(v1, v2) + if v1 == "latest" || v2 == "latest" + return 0 + end + + gem_v1 = Gem::Version.new(v1) + gem_v2 = Gem::Version.new(v2) + + gem_v1 <=> gem_v2 + end + def uninstall_registry_entries @uninstall_registry_entries ||= Chef::Provider::Package::Windows::RegistryUninstallEntry.find_entries(new_resource.package_name) end diff --git a/lib/chef/provider/package/yum.rb b/lib/chef/provider/package/yum.rb index d87e421409..7129104224 100644 --- a/lib/chef/provider/package/yum.rb +++ b/lib/chef/provider/package/yum.rb @@ -156,26 +156,6 @@ class Chef yum_command("-d0 -e0 -y#{expand_options(new_resource.options)} versionlock delete #{unlock_str}") end - # Keep upgrades from trying to install an older candidate version. Can happen when a new - # version is installed then removed from a repository, now the older available version - # shows up as a viable install candidate. - # - # Can be done in upgrade_package but an upgraded from->to log message slips out - # - # Hacky - better overall solution? Custom compare in Package provider? - def action_upgrade - # Could be uninstalled or have no candidate - if current_resource.version.nil? || !candidate_version_array.any? - super - elsif candidate_version_array.zip(current_version_array).any? do |c, i| - RPMVersion.parse(c) > RPMVersion.parse(i) - end - super - else - Chef::Log.debug("#{new_resource} is at the latest version - nothing to do") - end - end - private # @@ -190,6 +170,10 @@ class Chef end end + def version_compare(v1, v2) + RPMVersion.parse(v1) <=> RPMVersion.parse(v2) + end + # Enable or disable YumCache extra_repo_control def manage_extra_repo_control if new_resource.options diff --git a/lib/chef/provider/zypper_repository.rb b/lib/chef/provider/zypper_repository.rb index e6fd917d77..31cf8839b2 100644 --- a/lib/chef/provider/zypper_repository.rb +++ b/lib/chef/provider/zypper_repository.rb @@ -18,24 +18,25 @@ require "chef/resource" require "chef/dsl/declare_resource" -require "chef/mixin/which" require "chef/provider/noop" +require "chef/mixin/shell_out" require "shellwords" class Chef class Provider class ZypperRepository < Chef::Provider - - extend Chef::Mixin::Which - - provides :zypper_repository do - which "zypper" - end + provides :zypper_repository, platform_family: "suse" def load_current_resource end action :create do + if new_resource.gpgautoimportkeys + install_gpg_key(new_resource.gpgkey) + else + Chef::Log.debug("'gpgautoimportkeys' property is set to false. Skipping key import.") + end + declare_resource(:template, "/etc/zypp/repos.d/#{escaped_repo_name}.repo") do if template_available?(new_resource.source) source new_resource.source @@ -51,14 +52,14 @@ class Chef end action :delete do - declare_resource(:execute, "zypper removerepo #{escaped_repo_name}") do - only_if "zypper lr #{escaped_repo_name}" + declare_resource(:execute, "zypper --quiet --non-interactive removerepo #{escaped_repo_name}") do + only_if "zypper --quiet lr #{escaped_repo_name}" end end action :refresh do - declare_resource(:execute, "zypper#{' --gpg-auto-import-keys' if new_resource.gpgautoimportkeys} --quiet --no-confirm refresh --force #{escaped_repo_name}") do - only_if "zypper lr #{escaped_repo_name}" + declare_resource(:execute, "zypper --quiet --non-interactive refresh --force #{escaped_repo_name}") do + only_if "zypper --quiet lr #{escaped_repo_name}" end end @@ -66,14 +67,101 @@ class Chef alias_method :action_remove, :action_delete # zypper repos are allowed to have spaces in the names + # @return [String] escaped repo string def escaped_repo_name Shellwords.escape(new_resource.repo_name) end + # return the specified cookbook name or the cookbook containing the + # resource. + # + # @return [String] name of the cookbook + def cookbook_name + new_resource.cookbook || new_resource.cookbook_name + end + + # determine if a template file is available in the current run + # @param [String] path the path to the template file + # + # @return [Boolean] template file exists or doesn't def template_available?(path) - !path.nil? && run_context.has_template_in_cookbook?(new_resource.cookbook_name, path) + !path.nil? && run_context.has_template_in_cookbook?(cookbook_name, path) + end + + # determine if a cookbook file is available in the run + # @param [String] path the path to the template file + # + # @return [Boolean] cookbook file exists or doesn't + def has_cookbook_file?(fn) + run_context.has_cookbook_file_in_cookbook?(cookbook_name, fn) + end + + # Given the provided key URI determine what kind of chef resource we need + # to fetch the key + # @param [String] uri the uri of the gpg key (local path or http URL) + # + # @raise [Chef::Exceptions::FileNotFound] Key isn't remote or found in the current run + # + # @return [Symbol] :remote_file or :cookbook_file + def key_type(uri) + if uri.start_with?("http") + Chef::Log.debug("Will use :remote_file resource to cache the gpg key locally") + :remote_file + elsif has_cookbook_file?(uri) + Chef::Log.debug("Will use :cookbook_file resource to cache the gpg key locally") + :cookbook_file + else + raise Chef::Exceptions::FileNotFound, "Cannot determine location of gpgkey. Must start with 'http' or be a file managed by Chef." + end end + # is the provided key already installed + # @param [String] key_path the path to the key on the local filesystem + # + # @return [boolean] is the key already known by rpm + def key_installed?(key_path) + so = shell_out("rpm -qa gpg-pubkey*") + # expected output & match: http://rubular.com/r/RdF7EcXEtb + status = /gpg-pubkey-#{key_fingerprint(key_path)}/.match(so.stdout) + Chef::Log.debug("GPG key at #{key_path} is known by rpm? #{status ? "true" : "false"}") + status + end + + # extract the gpg key fingerprint from a local file + # @param [String] key_path the path to the key on the local filesystem + # + # @return [String] the fingerprint of the key + def key_fingerprint(key_path) + so = shell_out!("gpg --with-fingerprint #{key_path}") + # expected output and match: http://rubular.com/r/BpfMjxySQM + fingerprint = /pub\s*\S*\/(\S*)/.match(so.stdout)[1].downcase + Chef::Log.debug("GPG fingerprint of key at #{key_path} is #{fingerprint}") + fingerprint + end + + # install the provided gpg key + # @param [String] uri the uri of the local or remote gpg key + def install_gpg_key(uri) + unless uri + Chef::Log.debug("'gpgkey' property not provided or set to nil. Skipping key import.") + return + end + + cached_keyfile = ::File.join(Chef::Config[:file_cache_path], uri.split("/")[-1]) + + declare_resource(key_type(new_resource.gpgkey), cached_keyfile) do + source uri + mode "0644" + sensitive new_resource.sensitive + action :create + end + + declare_resource(:execute, "import gpg key from #{new_resource.gpgkey}") do + command "/bin/rpm --import #{cached_keyfile}" + not_if { key_installed?(cached_keyfile) } + action :run + end + end end end end diff --git a/lib/chef/resource/zypper_repository.rb b/lib/chef/resource/zypper_repository.rb index 69a96b42cf..88b6fd9336 100644 --- a/lib/chef/resource/zypper_repository.rb +++ b/lib/chef/resource/zypper_repository.rb @@ -39,6 +39,7 @@ class Chef property :mode, default: "0644" property :refresh_cache, [true, false], default: true property :source, String, regex: /.*/ + property :cookbook, String property :gpgautoimportkeys, [true, false], default: true default_action :create diff --git a/lib/chef/version.rb b/lib/chef/version.rb index 63fd85c63c..01139015cf 100644 --- a/lib/chef/version.rb +++ b/lib/chef/version.rb @@ -23,7 +23,7 @@ require "chef/version_string" class Chef CHEF_ROOT = File.expand_path("../..", __FILE__) - VERSION = Chef::VersionString.new("13.5.6") + VERSION = Chef::VersionString.new("13.5.8") end # diff --git a/spec/unit/provider/package/rubygems_spec.rb b/spec/unit/provider/package/rubygems_spec.rb index 856f8d460c..ac2b511ca9 100644 --- a/spec/unit/provider/package/rubygems_spec.rb +++ b/spec/unit/provider/package/rubygems_spec.rb @@ -383,6 +383,11 @@ describe Chef::Provider::Package::Rubygems do provider.load_current_resource expect(provider.target_version_already_installed?(provider.current_resource.version, new_resource.version)).to be_falsey end + + it "version_equals? should return false so that we can search for candidates" do + provider.load_current_resource + expect(provider.version_equals?(provider.current_resource.version, new_resource.version)).to be_falsey + end end describe "when new_resource version is an rspec version" do diff --git a/spec/unit/provider/zypper_repository_spec.rb b/spec/unit/provider/zypper_repository_spec.rb new file mode 100644 index 0000000000..a366a33e86 --- /dev/null +++ b/spec/unit/provider/zypper_repository_spec.rb @@ -0,0 +1,124 @@ +# +# Author:: Tim Smith (<tsmith@chef.io>) +# Copyright:: 2017, Chef Software, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +# Output of the command: +# => rpm -qa gpg-pubkey* +RPM_KEYS = <<-EOF +gpg-pubkey-307e3d54-4be01a65 +gpg-pubkey-3dbdc284-53674dd4 +EOF + +# Output of the command: +# => gpg --with-fingerprint [FILE] +GPG_FINGER = <<-EOF +pub 2048R/3DBDC284 2011-08-19 [expires: 2024-06-14] + Key fingerprint = 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62 +uid nginx signing key <signing-key@nginx.com> +EOF + +describe Chef::Provider::ZypperRepository do + let(:new_resource) { Chef::Resource::ZypperRepository.new("Nginx Repository") } + let(:provider) do + node = Chef::Node.new + events = Chef::EventDispatch::Dispatcher.new + run_context = Chef::RunContext.new(node, {}, events) + Chef::Provider::ZypperRepository.new(new_resource, run_context) + end + + let(:rpm_key_finger) do + double("shell_out_with_systems_locale", stdout: RPM_KEYS, exitstatus: 0, error?: false) + end + + let(:gpg_finger) do + double("shell_out_with_systems_locale", stdout: GPG_FINGER, exitstatus: 0, error?: false) + end + + it "responds to load_current_resource" do + expect(provider).to respond_to(:load_current_resource) + end + + describe "#action_create" do + it "skips key import if gpgautoimportkeys is false" do + new_resource.gpgautoimportkeys(false) + expect(provider).to receive(:declare_resource) + expect(Chef::Log).to receive(:debug) + provider.run_action(:create) + end + end + + describe "#escaped_repo_name" do + it "returns an escaped repo name" do + expect(provider.escaped_repo_name).to eq('Nginx\\ Repository') + end + end + + describe "#cookbook_name" do + it "returns 'test' when the cookbook property is set" do + new_resource.cookbook("test") + expect(provider.cookbook_name).to eq("test") + end + end + + describe "#key_type" do + it "returns :remote_file with an http URL" do + expect(provider.key_type("https://www.chef.io/key")).to eq(:remote_file) + end + + it "returns :cookbook_file with a chef managed file" do + expect(provider).to receive(:has_cookbook_file?).and_return(true) + expect(provider.key_type("/foo/nginx.key")).to eq(:cookbook_file) + end + + it "throws exception if an unknown file specified" do + expect(provider).to receive(:has_cookbook_file?).and_return(false) + expect { provider.key_type("/foo/nginx.key") }.to raise_error(Chef::Exceptions::FileNotFound) + end + end + + describe "#key_installed?" do + before do + expect(provider).to receive(:shell_out).with("rpm -qa gpg-pubkey*").and_return(rpm_key_finger) + end + + it "returns true if the key is installed" do + expect(provider).to receive(:key_fingerprint).and_return("3dbdc284") + expect(provider.key_installed?("/foo/nginx.key")).to be_truthy + end + + it "returns false if the key is not installed" do + expect(provider).to receive(:key_fingerprint).and_return("BOGUS") + expect(provider.key_installed?("/foo/nginx.key")).to be_falsey + end + end + + describe "#key_fingerprint" do + it "returns the key's fingerprint" do + expect(provider).to receive(:shell_out!).with("gpg --with-fingerprint /foo/nginx.key").and_return(gpg_finger) + expect(provider.key_fingerprint("/foo/nginx.key")).to eq("3dbdc284") + end + end + + describe "#install_gpg_key" do + it "skips installing the key if a nil value for key is passed" do + expect(Chef::Log).to receive(:debug) + provider.install_gpg_key(nil) + end + end +end diff --git a/spec/unit/resource/zypper_repository_spec.rb b/spec/unit/resource/zypper_repository_spec.rb index 16951d071c..de08b183a5 100644 --- a/spec/unit/resource/zypper_repository_spec.rb +++ b/spec/unit/resource/zypper_repository_spec.rb @@ -46,20 +46,22 @@ describe Chef::Resource::ZypperRepository do expect { resource.action :delete }.to raise_error(ArgumentError) end - it "should resolve to a Noop class when zypper is not found" do - expect(Chef::Provider::ZypperRepository).to receive(:which).with("zypper").and_return(false) + it "resolves to a Noop class when on non-linux OS" do + node.automatic[:os] = "windows" + node.automatic[:platform_family] = "windows" expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop) end - it "should resolve to a ZypperRepository class when zypper is found" do - expect(Chef::Provider::ZypperRepository).to receive(:which).with("zypper").and_return(true) - expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::ZypperRepository) + it "resolves to a Noop class when on non-suse linux" do + node.automatic[:os] = "linux" + node.automatic[:platform_family] = "debian" + expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop) end - end - context "on windows", :windows_only do - it "should resolve to a NoOp provider" do - expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::Noop) + it "resolves to a ZypperRepository class when on a suse platform_family" do + node.automatic[:os] = "linux" + node.automatic[:platform_family] = "suse" + expect(resource.provider_for_action(:add)).to be_a(Chef::Provider::ZypperRepository) end end end |