mirror of
https://github.com/inspec/inspec
synced 2024-11-10 23:24:18 +00:00
Merge pull request #5771 from inspec/nm/package-latest
Check for latest - package resource
This commit is contained in:
commit
97c4002e82
10 changed files with 124 additions and 12 deletions
|
@ -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 }
|
||||
|
|
|
@ -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,21 @@ 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
|
||||
raise Inspec::Exceptions::ResourceFailed, "Failed to fetch latest version. Error: #{cmd.stderr}"
|
||||
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 +153,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 +217,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 +258,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 +288,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 +321,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 +353,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 +406,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
|
||||
|
|
1
test/fixtures/cmd/apk-info-cmd
vendored
Normal file
1
test/fixtures/cmd/apk-info-cmd
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
git-2.18.4-r0 description:\nDistributed version control system\n\ngit-2.18.4-r0 webpage:\nhttps://www.git-scm.com/\n\ngit-2.18.4-r0 installed size:\n13213696\n\n
|
3
test/fixtures/cmd/apt-list-curl
vendored
Normal file
3
test/fixtures/cmd/apt-list-curl
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
Listing... Done
|
||||
curl/focal-updates,focal-security 7.68.0-1ubuntu2.7 amd64 [upgradable from: 7.68.0-1ubuntu2.4]
|
||||
curl/focal-updates,focal-security 7.68.0-1ubuntu2.7 i386
|
3
test/fixtures/cmd/get-pkg-versions
vendored
Normal file
3
test/fixtures/cmd/get-pkg-versions
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
Name Version Source ProviderName
|
||||
---- ------- ------ ------------
|
||||
Chef Client 12.12.15.1 Programs
|
1
test/fixtures/cmd/pacman-ss-grep-curl
vendored
Normal file
1
test/fixtures/cmd/pacman-ss-grep-curl
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
core/curl 7.80.0-1 [installed: 7.37.0-1]
|
1
test/fixtures/cmd/pkg-version-grep-vim-console
vendored
Normal file
1
test/fixtures/cmd/pkg-version-grep-vim-console
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
"vim-console-8.1.1954 = up-to-date with remote\n"
|
8
test/fixtures/cmd/yum-list-curl
vendored
Normal file
8
test/fixtures/cmd/yum-list-curl
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
Loaded plugins: fastestmirror
|
||||
Loading mirror speeds from cached hostfile
|
||||
* base: mirrors.piconets.webwerks.in
|
||||
* epel: repo.extreme-ix.org
|
||||
* extras: mirrors.piconets.webwerks.in
|
||||
* updates: mirrors.piconets.webwerks.in
|
||||
Installed Packages
|
||||
curl.x86_64 7.29.0-59.el7_9.1 @updates
|
|
@ -237,11 +237,15 @@ class MockLoader
|
|||
"/sbin/auditctl -l" => cmd.call("auditctl"),
|
||||
"/sbin/auditctl -s" => cmd.call("auditctl-s"),
|
||||
"dpkg -s curl" => cmd.call("dpkg-s-curl"),
|
||||
"apt list curl -a" => cmd.call("apt-list-curl"),
|
||||
"dpkg -s held-package" => cmd.call("dpkg-s-held-package"),
|
||||
"rpm -qi curl" => cmd.call("rpm-qi-curl"),
|
||||
"yum list curl" => cmd.call("yum-list-curl"),
|
||||
"Get-Package Chef Client v12.12.15 -AllVersions" => cmd.call("get-pkg-versions"),
|
||||
"rpm -qi --dbpath /var/lib/fake_rpmdb curl" => cmd.call("rpm-qi-curl"),
|
||||
"rpm -qi --dbpath /var/lib/rpmdb_does_not_exist curl" => cmd_exit_1.call,
|
||||
"pacman -Qi curl" => cmd.call("pacman-qi-curl"),
|
||||
"pacman -Ss curl | grep curl | grep installed" => cmd.call("pacman-ss-grep-curl"),
|
||||
"brew info --json=v1 curl" => cmd.call("brew-info--json-v1-curl"),
|
||||
"brew info --json=v1 nginx" => cmd.call("brew-info--json-v1-nginx"),
|
||||
"brew info --json=v1 nope" => cmd_exit_1.call,
|
||||
|
@ -387,6 +391,7 @@ class MockLoader
|
|||
"rpm -qa --queryformat '%{NAME} %{VERSION}-%{RELEASE} %{ARCH}\\n'" => cmd.call("rpm-qa-queryformat"),
|
||||
# pkg query all packages
|
||||
"pkg info vim-console" => cmd.call("pkg-info-vim-console"),
|
||||
"pkg version -v | grep vim-console" => cmd.call("pkg-version-grep-vim-console"),
|
||||
# port netstat on solaris 10 & 11
|
||||
"netstat -an -f inet -f inet6" => cmd.call("s11-netstat-an-finet-finet6"),
|
||||
# xinetd configuration
|
||||
|
@ -574,6 +579,7 @@ class MockLoader
|
|||
# alpine package commands
|
||||
"apk info -vv --no-network | grep git" => cmd.call("apk-info-grep-git"),
|
||||
"apk list --no-network --installed" => cmd.call("apk-info"),
|
||||
"apk info git" => cmd.call("apk-info-cmd"),
|
||||
|
||||
# filesystem command
|
||||
"2e7e0d4546342cee799748ec7e2b1c87ca00afbe590fa422a7c27371eefa88f0" => cmd.call("get-wmiobject-filesystem"),
|
||||
|
|
|
@ -6,25 +6,27 @@ describe "Inspec::Resources::Package" do
|
|||
# arch linux
|
||||
it "verify arch linux package parsing" do
|
||||
resource = MockLoader.new(:arch).load_resource("package", "curl")
|
||||
pkg = { name: "curl", installed: true, version: "7.37.0-1", type: "pacman" }
|
||||
pkg = { name: "curl", installed: true, version: "7.37.0-1", type: "pacman", only_version_no: "7.37.0" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.version).must_equal "7.37.0-1"
|
||||
_(resource.info).must_equal pkg
|
||||
_(resource.latest?).must_equal false
|
||||
end
|
||||
|
||||
# ubuntu
|
||||
it "verify ubuntu package parsing" do
|
||||
resource = MockLoader.new(:ubuntu).load_resource("package", "curl")
|
||||
pkg = { name: "curl", installed: true, held: false, version: "7.35.0-1ubuntu2", type: "deb" }
|
||||
pkg = { name: "curl", installed: true, held: false, version: "7.35.0-1ubuntu2", type: "deb", only_version_no: "7.35.0" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.held?).must_equal false
|
||||
_(resource.version).must_equal "7.35.0-1ubuntu2"
|
||||
_(resource.info).must_equal pkg
|
||||
_(resource.latest?).must_equal false
|
||||
end
|
||||
|
||||
it "verify ubuntu package which is held" do
|
||||
resource = MockLoader.new(:ubuntu).load_resource("package", "held-package")
|
||||
pkg = { name: "held-package", installed: true, held: true, version: "1.2.3-1", type: "deb" }
|
||||
pkg = { name: "held-package", installed: true, held: true, version: "1.2.3-1", type: "deb", only_version_no: "1.2.3" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.held?).must_equal true
|
||||
_(resource.version).must_equal "1.2.3-1"
|
||||
|
@ -34,7 +36,7 @@ describe "Inspec::Resources::Package" do
|
|||
# mint
|
||||
it "verify mint package parsing" do
|
||||
resource = MockLoader.new(:mint17).load_resource("package", "curl")
|
||||
pkg = { name: "curl", installed: true, held: false, version: "7.35.0-1ubuntu2", type: "deb" }
|
||||
pkg = { name: "curl", installed: true, held: false, version: "7.35.0-1ubuntu2", type: "deb", only_version_no: "7.35.0" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.version).must_equal "7.35.0-1ubuntu2"
|
||||
_(resource.info).must_equal pkg
|
||||
|
@ -48,6 +50,7 @@ describe "Inspec::Resources::Package" do
|
|||
installed: true,
|
||||
version: "7.29.0-19.el7",
|
||||
type: "rpm",
|
||||
only_version_no: "7.29.0",
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -56,6 +59,7 @@ describe "Inspec::Resources::Package" do
|
|||
_(resource.installed?).must_equal true
|
||||
_(resource.version).must_equal "7.29.0-19.el7"
|
||||
_(resource.info).must_equal pkg
|
||||
_(resource.latest?).must_equal true
|
||||
end
|
||||
|
||||
it "can build an `rpm` command containing `--dbpath`" do
|
||||
|
@ -91,7 +95,7 @@ describe "Inspec::Resources::Package" do
|
|||
# wrlinux
|
||||
it "verify wrlinux package parsing" do
|
||||
resource = MockLoader.new(:wrlinux).load_resource("package", "curl")
|
||||
pkg = { name: "curl", installed: true, version: "7.29.0-19.el7", type: "rpm" }
|
||||
pkg = { name: "curl", installed: true, version: "7.29.0-19.el7", type: "rpm", only_version_no: "7.29.0" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.version).must_equal "7.29.0-19.el7"
|
||||
_(resource.info).must_equal pkg
|
||||
|
@ -100,10 +104,11 @@ describe "Inspec::Resources::Package" do
|
|||
# windows
|
||||
it "verify windows package parsing" do
|
||||
resource = MockLoader.new(:windows).load_resource("package", "Chef Client v12.12.15")
|
||||
pkg = { name: "Chef Client v12.12.15 ", installed: true, version: "12.12.15.1", type: "windows" }
|
||||
pkg = { name: "Chef Client v12.12.15 ", installed: true, version: "12.12.15.1", type: "windows", only_version_no: "12.12.15.1" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.version).must_equal "12.12.15.1"
|
||||
_(resource.info).must_equal pkg
|
||||
_(resource.latest?).must_equal true
|
||||
end
|
||||
|
||||
# solaris 10
|
||||
|
@ -127,10 +132,11 @@ describe "Inspec::Resources::Package" do
|
|||
# darwin (brew)
|
||||
it "can parse ouptut from 'brew' when package is installed" do
|
||||
resource = MockLoader.new(:macos10_10).load_resource("package", "curl")
|
||||
pkg = { name: "curl", installed: true, version: "7.52.1", type: "brew" }
|
||||
pkg = { name: "curl", installed: true, version: "7.52.1", type: "brew", latest_version: "7.52.1", only_version_no: "7.52.1" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.version).must_equal "7.52.1"
|
||||
_(resource.info).must_equal pkg
|
||||
_(resource.latest?).must_equal true
|
||||
end
|
||||
|
||||
it "can parse ouptut from 'brew' when package is not installed but exists" do
|
||||
|
@ -152,19 +158,21 @@ describe "Inspec::Resources::Package" do
|
|||
# alpine
|
||||
it "can parse Alpine packages" do
|
||||
resource = MockLoader.new(:alpine).load_resource("package", "git")
|
||||
pkg = { name: "git", installed: true, version: "2.15.0-r1", type: "pkg" }
|
||||
pkg = { name: "git", installed: true, version: "2.15.0-r1", type: "pkg", only_version_no: "2.15.0" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.version).must_equal "2.15.0-r1"
|
||||
_(resource.info).must_equal pkg
|
||||
_(resource.latest?).must_equal false
|
||||
end
|
||||
|
||||
# freebsd
|
||||
it "can parse FreeBSD packages" do
|
||||
resource = MockLoader.new(:freebsd11).load_resource("package", "vim-console")
|
||||
pkg = { name: "vim-console", installed: true, version: "8.1.1954", type: "pkg" }
|
||||
pkg = { name: "vim-console", installed: true, version: "8.1.1954", type: "pkg", only_version_no: "8.1.1954" }
|
||||
_(resource.installed?).must_equal true
|
||||
_(resource.version).must_equal "8.1.1954"
|
||||
_(resource.info).must_equal pkg
|
||||
_(resource.latest?).must_equal true
|
||||
end
|
||||
|
||||
# undefined
|
||||
|
|
Loading…
Reference in a new issue