diff --git a/docs/resources/package.md.erb b/docs/resources/package.md.erb index 31fec2987..fa387aa0b 100644 --- a/docs/resources/package.md.erb +++ b/docs/resources/package.md.erb @@ -96,6 +96,12 @@ The following examples show how to use this InSpec audit resource. it { should_not be_running } 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 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: diff --git a/lib/resources/package.rb b/lib/resources/package.rb index 64174e83c..ad612118f 100644 --- a/lib/resources/package.rb +++ b/lib/resources/package.rb @@ -19,7 +19,7 @@ module Inspec::Resources end " - def initialize(package_name = nil) # rubocop:disable Metrics/AbcSize + def initialize(package_name = nil, opts = {}) # rubocop:disable Metrics/AbcSize @package_name = package_name @name = @package_name @cache = nil @@ -30,7 +30,7 @@ module Inspec::Resources if os.debian? @pkgman = Deb.new(inspec) elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family]) - @pkgman = Rpm.new(inspec) + @pkgman = Rpm.new(inspec, opts) elsif ['arch'].include?(os[:family]) @pkgman = Pacman.new(inspec) elsif ['darwin'].include?(os[:family]) @@ -46,6 +46,8 @@ module Inspec::Resources else return skip_resource 'The `package` resource is not supported on your OS yet.' end + + evaluate_missing_requirements end # returns true if the package is installed @@ -71,6 +73,14 @@ module Inspec::Resources def to_s "System Package #{@package_name}" 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 class PkgManagement @@ -78,6 +88,12 @@ module Inspec::Resources def initialize(inspec) @inspec = inspec end + + def missing_requirements + # Each provider can provide an Array of missing requirements that will be + # combined into a `skip_resource` message + [] + end end # Debian / Ubuntu @@ -104,8 +120,25 @@ module Inspec::Resources # RHEL family 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) - 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, # therefore we need to check for emptyness return nil if cmd.exit_status.to_i != 0 || cmd.stdout.chomp.empty? @@ -133,6 +166,17 @@ module Inspec::Resources type: 'rpm', } end + + private + + def rpm_command(package_name) + cmd = '' + cmd += 'rpm -qia' + cmd += " --dbpath #{@dbpath}" if @dbpath + cmd += ' ' + package_name + + cmd + end end # MacOS / Darwin implementation diff --git a/test/helper.rb b/test/helper.rb index 69242867b..17a1cd22c 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -153,6 +153,8 @@ class MockLoader # 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'), '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 @@ -165,6 +167,8 @@ class MockLoader mock.mock_command('', '', '', 0) } + cmd_exit_1 = mock.mock_command('', '', '', 1) + mock.commands = { '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'), @@ -181,6 +185,8 @@ class MockLoader 'yum -v repolist all' => cmd.call('yum-repolist-all'), 'dpkg -s curl' => cmd.call('dpkg-s-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'), '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'), diff --git a/test/unit/resources/package_test.rb b/test/unit/resources/package_test.rb index eae5d4242..413bef0d2 100644 --- a/test/unit/resources/package_test.rb +++ b/test/unit/resources/package_test.rb @@ -34,12 +34,42 @@ describe 'Inspec::Resources::Package' do end # centos - it 'verify centos package parsing' do - resource = MockLoader.new(:centos7).load_resource('package', 'curl') - pkg = { name: 'curl', installed: true, version: '7.29.0-19.el7', type: 'rpm' } - _(resource.installed?).must_equal true - _(resource.version).must_equal '7.29.0-19.el7' - _(resource.info).must_equal pkg + describe 'Rpm' do # rubocop:disable BlockLength + let(:pkg) do + { + name: 'curl', + installed: true, + 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 # hpux