service resource: attempt a SysV fallback if SystemD unit file is not found (#2473)

* service resource: Fix no `.service` + systemd bug

This modifies the `enabled?` check to fallback to `sysv_service` in the
event that a `.service` file cannot be found.

For example: On Debian 8.7 the stock apache2 package does not deploy a
`.service` file but deploys a SysV style service. This causes
`systemctl is-enabled` to fail when the service is in fact enabled.

* Remove `cmd_stderr` and clean up `cmd_exit_1`
* Clean up `stderr` assignment using ternary
This commit is contained in:
Jerry Aldrich 2018-01-23 12:34:47 -08:00 committed by Dominik Richter
parent acf9ce379d
commit 98546984ae
7 changed files with 55 additions and 4 deletions

View file

@ -250,7 +250,17 @@ module Inspec::Resources
end
def is_enabled?(service_name)
inspec.command("#{service_ctl} is-enabled #{service_name} --quiet").exit_status == 0
result = inspec.command("#{service_ctl} is-enabled #{service_name} --quiet")
return true if result.exit_status == 0
# Some systems may not have a `.service` file for a particular service
# which causes the `systemctl is-enabled` check to fail despite the
# service being enabled. In that event we fallback to `sysv_service`.
if result.stderr =~ /Failed to get.*No such file or directory/
return inspec.sysv_service(service_name).enabled?
end
false
end
def is_active?(service_name)

View file

@ -201,7 +201,10 @@ class MockLoader
mock.mock_command('', '', '', 0)
}
cmd_exit_1 = mock.mock_command('', '', '', 1)
cmd_exit_1 = lambda { |x = nil|
stderr = x.nil? ? '' : File.read(File.join(scriptpath, 'unit/mock/cmd', x))
mock.mock_command('', '', stderr, 1)
}
mock.commands = {
'' => empty.call,
@ -234,7 +237,7 @@ class MockLoader
'dpkg -s held-package' => cmd.call('dpkg-s-held-package'),
'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,
'rpm -qia --dbpath /var/lib/rpmdb_does_not_exist curl' => cmd_exit_1.call,
'pacman -Qi curl' => cmd.call('pacman-qi-curl'),
'brew info --json=v1 curl' => cmd.call('brew-info--json-v1-curl'),
'/usr/local/bin/brew info --json=v1 curl' => cmd.call('brew-info--json-v1-curl'),
@ -248,7 +251,7 @@ class MockLoader
"Rscript -e 'packageVersion(\"DBI\")'" => cmd.call('r-print-version'),
"Rscript -e 'packageVersion(\"DoesNotExist\")'" => cmd.call('r-print-version-not-installed'),
"perl -le 'eval \"require $ARGV[0]\" and print $ARGV[0]->VERSION or exit 1' DBD::Pg" => cmd.call('perl-print-version'),
"perl -le 'eval \"require $ARGV[0]\" and print $ARGV[0]->VERSION or exit 1' DOES::Not::Exist" => cmd_exit_1,
"perl -le 'eval \"require $ARGV[0]\" and print $ARGV[0]->VERSION or exit 1' DOES::Not::Exist" => cmd_exit_1.call,
'pip show jinja2' => cmd.call('pip-show-jinja2'),
'pip show django' => cmd.call('pip-show-django'),
'/test/path/pip show django' => cmd.call('pip-show-non-standard-django'),
@ -281,6 +284,7 @@ class MockLoader
'initctl --version' => cmd.call('initctl--version'),
# show ssh service Centos 7
'systemctl show --all sshd' => cmd.call('systemctl-show-all-sshd'),
'systemctl show --all apache2' => cmd.call('systemctl-show-all-apache2'),
'/path/to/systemctl show --all sshd' => cmd.call('systemctl-show-all-sshd'),
'systemctl show --all dbus' => cmd.call('systemctl-show-all-dbus'),
'/path/to/systemctl show --all dbus' => cmd.call('systemctl-show-all-dbus'),
@ -454,13 +458,16 @@ class MockLoader
"bash -c 'type \"firewall-cmd\"'" => cmd.call('firewall-cmd'),
'rpm -qia firewalld' => cmd.call('pkg-info-firewalld'),
'systemctl is-active sshd --quiet' => empty.call,
'systemctl is-active apache2 --quiet' => empty.call,
'systemctl is-enabled sshd --quiet' => empty.call,
'systemctl is-enabled apache2 --quiet' => cmd_exit_1.call('systemctl-is-enabled-apache2-stderr'),
'systemctl is-active dbus --quiet' => empty.call,
'systemctl is-enabled dbus --quiet' => empty.call,
'/path/to/systemctl is-active sshd --quiet' => empty.call,
'/path/to/systemctl is-enabled sshd --quiet' => empty.call,
'/usr/sbin/service sshd status' => empty.call,
'/sbin/service sshd status' => empty.call,
'service apache2 status' => cmd_exit_1.call,
'type "lsof"' => empty.call,
# http resource - remote worker'

View file

@ -10,3 +10,7 @@
/etc/rc.d/rc4.d/S08ip6tables
/etc/rc.d/rc4.d/S90crond
/etc/rc.d/rc4.d/S55sshd
/etc/rc2.d/S02apache2
/etc/rc3.d/S02apache2
/etc/rc4.d/S02apache2
/etc/rc5.d/S02apache2

View file

@ -1,2 +1,3 @@
crond
sshd
apache2

View file

@ -0,0 +1 @@
Failed to get unit file state for apache2.service: No such file or directory

View file

@ -0,0 +1,7 @@
Id=apache2.service
Names=apache2.service
Description=LSB: Apache2 web server
LoadState=loaded
UnitFileState=
SubState=running
ActiveState=active

View file

@ -268,6 +268,27 @@ describe 'Inspec::Resources::Service' do
_(resource.params).must_equal params
end
# debian 8 with systemd but no service file
it 'gets the correct service info when the `.service` file is missing' do
resource = MockLoader.new(:debian8).load_resource('service', 'apache2')
params = Hashie::Mash.new(
'ActiveState' => 'active',
'Description' => 'LSB: Apache2 web server',
'Id' => 'apache2.service',
'LoadState' => 'loaded',
'Names' => 'apache2.service',
'SubState' => 'running',
'UnitFileState' => ''
)
_(resource.type).must_equal 'systemd'
_(resource.name).must_equal 'apache2.service'
_(resource.description).must_equal 'LSB: Apache2 web server'
_(resource.installed?).must_equal true
_(resource.enabled?).must_equal true
_(resource.running?).must_equal true
_(resource.params).must_equal params
end
# macos test
it 'verify mac osx service parsing' do
resource = MockLoader.new(:osx104).load_resource('service', 'ssh')