Matcher be_latest added for package resource and documentation

Signed-off-by: Nikita Mathur <nikita.mathur@chef.io>
This commit is contained in:
Nikita Mathur 2021-12-20 18:07:22 +05:30
parent a0875c9786
commit ff2f7f4b7b
2 changed files with 85 additions and 3 deletions

View file

@ -78,7 +78,7 @@ The following examples show how to use this Chef InSpec audit resource.
its('telnet') { should eq nil }
end
### Test if ClamAV (an antivirus engine) is installed and running
### Test if ClamAV (an antivirus engine) is installed, latest and running
describe package('clamav') do
it { should be_installed }
@ -88,6 +88,7 @@ The following examples show how to use this Chef InSpec audit resource.
describe service('clamd') do
it { should be_enabled }
it { should be_installed }
it { should be_latest }
it { should be_running }
end
@ -97,7 +98,7 @@ The following examples show how to use this Chef InSpec audit resource.
it { should be_installed }
end
### Verify if Memcached is installed, enabled, and running
### Verify if Memcached is installed, latest, enabled, and running
Memcached is an in-memory key-value store that helps improve the performance of database-driven websites and can be installed, maintained, and tested using the `memcached` cookbook (maintained by Chef). The following example is from the `memcached` cookbook and shows how to use a combination of the `package`, `service`, and `port` Chef InSpec audit resources to test if Memcached is installed, enabled, and running:
@ -107,6 +108,7 @@ Memcached is an in-memory key-value store that helps improve the performance of
describe service('memcached') do
it { should be_installed }
it { should be_latest }
it { should be_enabled }
it { should be_running }
end
@ -131,3 +133,9 @@ will not be upgraded to a later version.
The `be_installed` matcher tests if the named package is installed on the system:
it { should be_installed }
### be_latest
The `be_latest` matcher tests if the named installed package is latest on the system. It is not supported in Oracle Solaris, IBM AIX and HP UX operating systems.
it { should be_latest }

View file

@ -26,6 +26,7 @@ module Inspec::Resources
@cache = nil
# select package manager
@pkgman = nil
@latest_version = nil
os = inspec.os
if os.debian?
@ -60,6 +61,15 @@ module Inspec::Resources
info[:installed] == true
end
def latest?(_provider = nil, _version = nil)
os = inspec.os
if os.solaris? || (%w{hpux aix}.include? os[:family])
raise Inspec::Exceptions::ResourceSkipped, "The `be_latest` matcher is not supported on your OS yet."
end
(!info[:only_version_no].nil? && !latest_version.nil?) && (info[:only_version_no] == latest_version)
end
# returns true it the package is held (if the OS supports it)
def held?(_provider = nil, _version = nil)
info[:held] == true
@ -82,6 +92,10 @@ module Inspec::Resources
info[:version]
end
def latest_version
@latest_version ||= ( @pkgman.latest_version(@package_name) || info[:latest_version] )
end
def to_s
"System Package #{@package_name}"
end
@ -107,6 +121,22 @@ module Inspec::Resources
# combined into a `ResourceSkipped` exception message.
[]
end
private
def fetch_latest_version(cmd_string)
cmd = inspec.command(cmd_string)
if cmd.exit_status != 0
Inspec::Log.error "Failed to fetch latest version. Error: #{cmd.stderr}"
nil
else
fetch_version_no(cmd.stdout)
end
end
def fetch_version_no(output)
output.scan(/(?:(?:\d+|[a-z])[.]){2,}(?:\d+|[a-z]*)(?:[a-z]*)(?:[0-9]*)/).max_by { |s| Gem::Version.new(s) } unless output.nil?
end
end
# Debian / Ubuntu
@ -124,14 +154,21 @@ module Inspec::Resources
# If the package is installed and marked hold, Status is "hold ok installed"
# If the package is removed and not purged, Status is "deinstall ok config-files" with exit_status 0
# If the package is purged cmd fails with non-zero exit status
{
name: params["Package"],
installed: params["Status"].split(" ")[2] == "installed",
held: params["Status"].split(" ")[0] == "hold",
version: params["Version"],
type: "deb",
only_version_no: fetch_version_no(params["Version"]),
}
end
def latest_version(package_name)
cmd_string = "apt list #{package_name} -a"
fetch_latest_version(cmd_string)
end
end
# RHEL family
@ -181,9 +218,15 @@ module Inspec::Resources
installed: true,
version: "#{v}-#{r}",
type: "rpm",
only_version_no: "#{v}",
}
end
def latest_version(package_name)
cmd_string = "yum list #{package_name}"
fetch_latest_version(cmd_string)
end
private
def rpm_command(package_name)
@ -216,11 +259,17 @@ module Inspec::Resources
installed: true,
version: pkg["installed"][0]["version"],
type: "brew",
latest_version: pkg["versions"]["stable"],
only_version_no: pkg["installed"][0]["version"],
}
rescue JSON::ParserError => e
raise Inspec::Exceptions::ResourceFailed,
"Failed to parse JSON from `brew` command. Error: #{e}"
end
def latest_version(package_name)
nil
end
end
# Arch Linux
@ -240,8 +289,14 @@ module Inspec::Resources
installed: true,
version: params["Version"],
type: "pacman",
only_version_no: fetch_version_no(params["Version"]),
}
end
def latest_version(package_name)
cmd_string = "pacman -Ss #{package_name} | grep #{package_name} | grep installed"
fetch_latest_version(cmd_string)
end
end
class HpuxPkg < PkgManagement
@ -267,13 +322,20 @@ module Inspec::Resources
pkg_info = cmd.stdout.split("\n").delete_if { |e| e =~ /^WARNING/i }
pkg = pkg_info[0].split(" - ")[0]
version = pkg.partition("-")[2]
{
name: pkg.partition("-")[0],
installed: true,
version: pkg.partition("-")[2],
version: version,
type: "pkg",
only_version_no: fetch_version_no(version),
}
end
def latest_version(package_name)
cmd_string = "apk info #{package_name}"
fetch_latest_version(cmd_string)
end
end
class FreebsdPkg < PkgManagement
@ -292,8 +354,14 @@ module Inspec::Resources
installed: true,
version: params["Version"],
type: "pkg",
only_version_no: params["Version"],
}
end
def latest_version(package_name)
cmd_string = "pkg version -v | grep #{package_name}"
fetch_latest_version(cmd_string)
end
end
# Determines the installed packages on Windows using the Windows package registry entries.
@ -339,8 +407,14 @@ module Inspec::Resources
installed: true,
version: package["DisplayVersion"],
type: "windows",
only_version_no: package["DisplayVersion"],
}
end
def latest_version(package_name)
cmd_string = "Get-Package #{package_name} -AllVersions"
fetch_latest_version(cmd_string)
end
end
# AIX