Add rpm_dbpath support to the package resource (#1960)

Signed-off-by: Jerry Aldrich III <jerry@chef.io>
This commit is contained in:
Jerry Aldrich III 2017-06-28 04:21:15 -06:00 committed by Christoph Hartmann
parent a0ebc43132
commit cc6f1e90ca
4 changed files with 95 additions and 9 deletions

View file

@ -96,6 +96,12 @@ The following examples show how to use this InSpec audit resource.
it { should_not be_running } it { should_not be_running }
end end
### Verify if some_package is installed according to my_rpmdb
describe package('some_package', rpm_dbpath: '/var/lib/my_rpmdb') do
it { should be_installed }
end
### Verify if Memcached is installed, enabled, and running ### Verify if Memcached is installed, 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` InSpec audit resources to test if Memcached is installed, 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` InSpec audit resources to test if Memcached is installed, enabled, and running:

View file

@ -19,7 +19,7 @@ module Inspec::Resources
end end
" "
def initialize(package_name = nil) # rubocop:disable Metrics/AbcSize def initialize(package_name = nil, opts = {}) # rubocop:disable Metrics/AbcSize
@package_name = package_name @package_name = package_name
@name = @package_name @name = @package_name
@cache = nil @cache = nil
@ -30,7 +30,7 @@ module Inspec::Resources
if os.debian? if os.debian?
@pkgman = Deb.new(inspec) @pkgman = Deb.new(inspec)
elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family]) elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family])
@pkgman = Rpm.new(inspec) @pkgman = Rpm.new(inspec, opts)
elsif ['arch'].include?(os[:family]) elsif ['arch'].include?(os[:family])
@pkgman = Pacman.new(inspec) @pkgman = Pacman.new(inspec)
elsif ['darwin'].include?(os[:family]) elsif ['darwin'].include?(os[:family])
@ -46,6 +46,8 @@ module Inspec::Resources
else else
return skip_resource 'The `package` resource is not supported on your OS yet.' return skip_resource 'The `package` resource is not supported on your OS yet.'
end end
evaluate_missing_requirements
end end
# returns true if the package is installed # returns true if the package is installed
@ -71,6 +73,14 @@ module Inspec::Resources
def to_s def to_s
"System Package #{@package_name}" "System Package #{@package_name}"
end end
private
def evaluate_missing_requirements
missing_requirements_string = @pkgman.missing_requirements.uniq.join(', ')
return if missing_requirements_string.empty?
skip_resource "The following requirements are not met for this resource: #{missing_requirements_string}"
end
end end
class PkgManagement class PkgManagement
@ -78,6 +88,12 @@ module Inspec::Resources
def initialize(inspec) def initialize(inspec)
@inspec = inspec @inspec = inspec
end end
def missing_requirements
# Each provider can provide an Array of missing requirements that will be
# combined into a `skip_resource` message
[]
end
end end
# Debian / Ubuntu # Debian / Ubuntu
@ -104,8 +120,25 @@ module Inspec::Resources
# RHEL family # RHEL family
class Rpm < PkgManagement class Rpm < PkgManagement
def initialize(inspec, opts)
super(inspec)
@dbpath = opts.fetch(:rpm_dbpath, nil)
end
def missing_requirements
missing_requirements = []
unless @dbpath.nil? || inspec.directory(@dbpath).directory?
missing_requirements << "RPMDB #{@dbpath} does not exist"
end
missing_requirements
end
def info(package_name) def info(package_name)
cmd = inspec.command("rpm -qia #{package_name}") rpm_cmd = rpm_command(package_name)
cmd = inspec.command(rpm_cmd)
# CentOS does not return an error code if the package is not installed, # CentOS does not return an error code if the package is not installed,
# therefore we need to check for emptyness # therefore we need to check for emptyness
return nil if cmd.exit_status.to_i != 0 || cmd.stdout.chomp.empty? return nil if cmd.exit_status.to_i != 0 || cmd.stdout.chomp.empty?
@ -133,6 +166,17 @@ module Inspec::Resources
type: 'rpm', type: 'rpm',
} }
end end
private
def rpm_command(package_name)
cmd = ''
cmd += 'rpm -qia'
cmd += " --dbpath #{@dbpath}" if @dbpath
cmd += ' ' + package_name
cmd
end
end end
# MacOS / Darwin implementation # MacOS / Darwin implementation

View file

@ -153,6 +153,8 @@ class MockLoader
# Test DH parameters, 2048 bit long safe prime, generator 2 for dh_params in PEM format # Test DH parameters, 2048 bit long safe prime, generator 2 for dh_params in PEM format
'dh_params.dh_pem' => mockfile.call('dh_params.dh_pem'), 'dh_params.dh_pem' => mockfile.call('dh_params.dh_pem'),
'default.toml' => mockfile.call('default.toml'), 'default.toml' => mockfile.call('default.toml'),
'/var/lib/fake_rpmdb' => mockdir.call(true),
'/var/lib/rpmdb_does_not_exist' => mockdir.call(false),
} }
# create all mock commands # create all mock commands
@ -165,6 +167,8 @@ class MockLoader
mock.mock_command('', '', '', 0) mock.mock_command('', '', '', 0)
} }
cmd_exit_1 = mock.mock_command('', '', '', 1)
mock.commands = { mock.commands = {
'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command' => cmd.call('ps-axo'), 'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command' => cmd.call('ps-axo'),
'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command' => cmd.call('ps-axoZ'), 'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command' => cmd.call('ps-axoZ'),
@ -181,6 +185,8 @@ class MockLoader
'yum -v repolist all' => cmd.call('yum-repolist-all'), 'yum -v repolist all' => cmd.call('yum-repolist-all'),
'dpkg -s curl' => cmd.call('dpkg-s-curl'), 'dpkg -s curl' => cmd.call('dpkg-s-curl'),
'rpm -qia curl' => cmd.call('rpm-qia-curl'), 'rpm -qia curl' => cmd.call('rpm-qia-curl'),
'rpm -qia --dbpath /var/lib/fake_rpmdb curl' => cmd.call('rpm-qia-curl'),
'rpm -qia --dbpath /var/lib/rpmdb_does_not_exist curl' => cmd_exit_1,
'pacman -Qi curl' => cmd.call('pacman-qi-curl'), 'pacman -Qi curl' => cmd.call('pacman-qi-curl'),
'brew info --json=v1 curl' => cmd.call('brew-info--json-v1-curl'), 'brew info --json=v1 curl' => cmd.call('brew-info--json-v1-curl'),
'gem list --local -a -q ^not-installed$' => cmd.call('gem-list-local-a-q-not-installed'), 'gem list --local -a -q ^not-installed$' => cmd.call('gem-list-local-a-q-not-installed'),

View file

@ -34,12 +34,42 @@ describe 'Inspec::Resources::Package' do
end end
# centos # centos
it 'verify centos package parsing' do describe 'Rpm' do # rubocop:disable BlockLength
resource = MockLoader.new(:centos7).load_resource('package', 'curl') let(:pkg) do
pkg = { name: 'curl', installed: true, version: '7.29.0-19.el7', type: 'rpm' } {
_(resource.installed?).must_equal true name: 'curl',
_(resource.version).must_equal '7.29.0-19.el7' installed: true,
_(resource.info).must_equal pkg version: '7.29.0-19.el7',
type: 'rpm',
}
end
it 'can parse RPM package info' do
resource = MockLoader.new(:centos7).load_resource('package', 'curl')
_(resource.installed?).must_equal true
_(resource.version).must_equal '7.29.0-19.el7'
_(resource.info).must_equal pkg
end
it 'can build an `rpm` command containing `--dbpath`' do
resource = MockLoader.new(:centos7).load_resource(
'package',
'curl',
rpm_dbpath: '/var/lib/fake_rpmdb',
)
_(resource.installed?).must_equal true
_(resource.version).must_equal '7.29.0-19.el7'
_(resource.info).must_equal pkg
end
it 'can add to `resource_skipped` when `--rpmdb` path does not exist' do
resource = MockLoader.new(:centos7).load_resource(
'package',
'curl',
rpm_dbpath: '/var/lib/rpmdb_does_not_exist',
)
_(resource.resource_skipped).wont_equal nil
end
end end
# hpux # hpux