-
Notifications
You must be signed in to change notification settings - Fork 682
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This pull request adds a packages resource so that we can check for pattern matches against all the packages on a system. This initially implements only dpkg support for debian-based platforms so we can cover this use case: ```ruby describe packages(/^xserver-xorg.*/) do its("list") { should be_empty } end ``` This uses FilterTable so we can supply additional queries, too. ```ruby describe packages(/vi.+/).where { status != 'installed' } do its('statuses') { should be_empty } end ``` Users can specify the name as a string or a regular expression. If it is a string, we will escape it and convert it to a regular expression to use in matching against the full returned list of packages. If it is a regular expression, we take that as is and use it to filter the results. While some package management systems such as `dpkg` can take a shell glob argument to filter their results, we eschew this and require a regular expression to match multiple package names because we will need this to work across other platforms in the future. This means that the following: ```ruby packages("vim") ``` Will return *all* the "vim" packages on the system. The `packages` resource will take `"vim"`, turn it into `/vim/`, and greedily match anything with "vim" in the name. To match only a single package named `vim`, it needs to be an anchored regular expression. ```ruby packages(/^vim$/) ``` Signed-off-by: Joshua Timberman <[email protected]> Use entries instead of list Added a few more tests and non installed package in output Signed-off-by: Alex Pop <[email protected]> fix lint Signed-off-by: Alex Pop <[email protected]> Signed-off-by: Joshua Timberman <[email protected]>
- Loading branch information
Showing
5 changed files
with
161 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# encoding: utf-8 | ||
# copyright: 2017, Chef Software, Inc. <[email protected]> | ||
# author: Joshua Timberman | ||
# author: Alex Pop | ||
# license: All rights reserved | ||
|
||
require 'utils/filter' | ||
|
||
module Inspec::Resources | ||
class Packages < Inspec.resource(1) | ||
name 'packages' | ||
desc 'Use the packages InSpec audit resource to test properties for multiple packages installed on the system' | ||
example " | ||
describe packages(/xserver-xorg.*/) do | ||
its('entries') { should be_empty } | ||
end | ||
describe packages('vim').entries.length do | ||
it { should be > 1 } | ||
end | ||
describe packages(/vi.+/).where { status != 'installed' } do | ||
its('statuses') { should be_empty } | ||
end | ||
" | ||
|
||
def initialize(pattern) | ||
@pattern = pattern_regexp(pattern) | ||
all_pkgs = package_list | ||
@list = all_pkgs.find_all do |hm| | ||
hm[:name] =~ pattern_regexp(pattern) | ||
end | ||
end | ||
|
||
def to_s | ||
"Packages #{@pattern.class == String ? @pattern : @pattern.inspect}" | ||
end | ||
|
||
filter = FilterTable.create | ||
filter.add_accessor(:where) | ||
.add_accessor(:entries) | ||
.add(:statuses, field: 'status', style: :simple) | ||
.add(:names, field: 'name') | ||
.add(:versions, field: 'version') | ||
.connect(self, :filtered_packages) | ||
|
||
private | ||
|
||
def pattern_regexp(p) | ||
if p.class == String | ||
Regexp.new(Regexp.escape(p)) | ||
elsif p.class == Regexp | ||
p | ||
else | ||
fail 'invalid name argument to packages resource, please use a "string" or /regexp/' | ||
end | ||
end | ||
|
||
def filtered_packages | ||
@list | ||
end | ||
|
||
def package_list | ||
os = inspec.os | ||
|
||
if os.debian? | ||
command = "dpkg-query -W -f='${db:Status-Abbrev} ${Package} ${Version}\\n'" | ||
else | ||
fail "packages resource is not yet supported on #{os.name}" | ||
end | ||
build_package_list(command) | ||
end | ||
|
||
Package = Struct.new(:status, :name, :version) | ||
|
||
def build_package_list(command) | ||
cmd = inspec.command(command) | ||
all = cmd.stdout.split("\n")[1..-1] | ||
return [] if all.nil? | ||
all.map do |m| | ||
a = m.split | ||
a[0] = 'installed' if a[0] =~ /^.i/ | ||
a[2] = a[2].split(':').last | ||
Package.new(*a) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
ii bash 4.3-14ubuntu1.1 | ||
rc fakeroot 1.20.2-1ubuntu1 | ||
rc libfakeroot 1.20.2-1ubuntu1 | ||
ii overlayroot 0.27ubuntu1.2 | ||
ii vim 2:7.4.1689-3ubuntu1.2 | ||
ii vim-common 2:7.4.1689-3ubuntu1.2 | ||
ii xorg 1:7.7+13ubuntu3 | ||
ii xorg-docs-core 1:1.7.1-1ubuntu1 | ||
ii xserver-common 2:1.18.4-0ubuntu0.2 | ||
ii xserver-xorg 1:7.7+13ubuntu3 | ||
ii xserver-xorg-core 2:1.18.4-0ubuntu0.2 | ||
ii xserver-xorg-input-all 1:7.7+13ubuntu3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# encoding: utf-8 | ||
# author: Joshua Timberman | ||
|
||
require 'helper' | ||
require 'inspec/resource' | ||
|
||
describe 'Inspec::Resources::Packages' do | ||
it 'verify packages resource' do | ||
resource = MockLoader.new(:ubuntu1604).load_resource('packages', /^vim$/) | ||
_(resource.entries.length).must_equal 1 | ||
_(resource.entries[0].to_h).must_equal({ | ||
status: 'installed', | ||
name: 'vim', | ||
version: '7.4.1689-3ubuntu1.2', | ||
}) | ||
end | ||
|
||
it 'package name matches with output (string)' do | ||
resource = MockLoader.new(:ubuntu1604).load_resource('packages', 'xserver-xorg') | ||
_(resource.to_s).must_equal 'Packages /xserver\\-xorg/' | ||
end | ||
|
||
it 'packages using where filters' do | ||
resource = MockLoader.new(:ubuntu1604).load_resource('packages', /.+root$/) | ||
_(resource.entries.length).must_equal 3 | ||
_(resource.where { status != 'installed' }.names).must_equal(['fakeroot', 'libfakeroot']) | ||
_(resource.where { version =~ /^0\.2.+/ }.entries[0].to_h).must_equal({ | ||
status: "installed", | ||
name: "overlayroot", | ||
version: "0.27ubuntu1.2", | ||
}) | ||
end | ||
|
||
it 'package name matches with output (regex)' do | ||
resource = MockLoader.new(:ubuntu1604).load_resource('packages', /vim/) | ||
_(resource.to_s).must_equal 'Packages /vim/' | ||
end | ||
|
||
it 'returns a list of packages with a wildcard' do | ||
resource = MockLoader.new(:ubuntu1604).load_resource('packages', /^xserver-xorg.*/) | ||
_(resource.statuses).must_equal ['installed'] | ||
_(resource.entries.length).must_equal 3 | ||
end | ||
|
||
|
||
it 'fails on non debian platforms' do | ||
proc { | ||
resource = MockLoader.new(:centos6).load_resource('packages', 'bash') | ||
resource.send(:entries, nil) | ||
}.must_raise(RuntimeError) | ||
end | ||
|
||
it 'fails if the packages name is not a string or regexp' do | ||
proc { | ||
resources = MockLoader.new(:ubuntu1604).load_resource('packages', [:a, :b]) | ||
resources.send(:entries, nil) | ||
}.must_raise(RuntimeError) | ||
end | ||
end |