mirror of
https://github.com/inspec/inspec
synced 2024-11-23 05:03:07 +00:00
Merge pull request #5981 from inspec/ss/enhance-service-resource
CFINSPEC-93: Enhance `service` resource
This commit is contained in:
commit
7fddb8c772
5 changed files with 160 additions and 26 deletions
|
@ -11,55 +11,62 @@ platform = "os"
|
||||||
parent = "inspec/resources/os"
|
parent = "inspec/resources/os"
|
||||||
+++
|
+++
|
||||||
|
|
||||||
Use the `service` Chef InSpec audit resource to test if the named service is installed, running and/or enabled.
|
Use the `service` Chef InSpec audit resource to test whether the installation of the named service is successful and enabled.
|
||||||
|
|
||||||
Under some circumstances, it may be necessary to specify the service manager by using one of the following service manager-specific resources: `bsd_service`, `launchd_service`, `runit_service`, `systemd_service`, `sysv_service`, or `upstart_service`. These resources are based on the `service` resource.
|
It may be necessary to specify the service manager by using one of the following service manager-specific resources: `bsd_service`, `launchd_service`, `runit_service`, `systemd_service`, `sysv_service`, and `upstart_service`. These resources are based on the `service` resource.
|
||||||
|
|
||||||
## Availability
|
## Availability
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
This resource is distributed along with Chef InSpec itself. You can use it automatically.
|
The Chef InSpec distributes this resource.
|
||||||
|
|
||||||
### Version
|
### Version
|
||||||
|
|
||||||
This resource first became available in v1.0.0 of InSpec.
|
This resource is available from Chef Inspec version 1.0.0.
|
||||||
|
|
||||||
## Syntax
|
## Syntax
|
||||||
|
|
||||||
A `service` resource block declares the name of a service and then one (or more) matchers to test the state of the service:
|
A `service` resource block declares the name of a service and one or more matchers to test the service state.
|
||||||
|
|
||||||
describe service('service_name') do
|
```ruby
|
||||||
|
describe service('NAME') do
|
||||||
it { should be_installed }
|
it { should be_installed }
|
||||||
it { should be_enabled }
|
it { should be_enabled }
|
||||||
it { should be_running }
|
it { should be_running }
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
where
|
> where
|
||||||
|
>
|
||||||
- `('service_name')` must specify a service name
|
> - `('service_name')` must specify a service name
|
||||||
- `be_installed`, `be_enabled`, and `be_running` are valid matchers for this resource
|
> - `be_installed`, `be_enabled`, and `be_running` are valid matchers for this resource
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
The following examples show how to use this Chef InSpec audit resource.
|
The following examples show how to use this Chef InSpec audit resource.
|
||||||
|
|
||||||
### Test if the postgresql service is both running and enabled
|
### Test if the PostgreSQL service is both running and enabled
|
||||||
|
|
||||||
describe service('postgresql') do
|
```ruby
|
||||||
|
describe service('PostgreSQL') do
|
||||||
it { should be_enabled }
|
it { should be_enabled }
|
||||||
it { should be_running }
|
it { should be_running }
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Test if the mysql service is both running and enabled
|
### Test if the MYSQL service is running and enabled
|
||||||
|
|
||||||
describe service('mysqld') do
|
```ruby
|
||||||
|
describe service('MYSQL') do
|
||||||
it { should be_enabled }
|
it { should be_enabled }
|
||||||
it { should be_running }
|
it { should be_running }
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Test if ClamAV (an antivirus engine) is installed and running
|
### Test if the installation of ClamAV (an antivirus engine) is successful and running
|
||||||
|
|
||||||
|
```ruby
|
||||||
describe package('clamav') do
|
describe package('clamav') do
|
||||||
it { should be_installed }
|
it { should be_installed }
|
||||||
its('version') { should eq '0.98.7' }
|
its('version') { should eq '0.98.7' }
|
||||||
|
@ -70,62 +77,93 @@ The following examples show how to use this Chef InSpec audit resource.
|
||||||
it { should_not be_installed }
|
it { should_not be_installed }
|
||||||
it { should_not be_running }
|
it { should_not be_running }
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Test Unix System V run levels
|
### Test Unix SystemV run levels
|
||||||
|
|
||||||
On targets that are using SystemV services, the existing run levels can also be checked:
|
On targets that are using SystemV services, the existing run levels can also be checked:
|
||||||
|
|
||||||
describe service('sshd').runlevels do
|
```ruby
|
||||||
|
describe service('SSH').runlevels do
|
||||||
its('keys') { should include(2) }
|
its('keys') { should include(2) }
|
||||||
end
|
end
|
||||||
|
|
||||||
describe service('sshd').runlevels(2,4) do
|
describe service('SSH').runlevels(2,4) do
|
||||||
it { should be_enabled }
|
it { should be_enabled }
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Override the service manager
|
### Override the service manager
|
||||||
|
|
||||||
Under some circumstances, it may be required to override the logic in place to select the right service manager. For example, to check a service managed by Upstart:
|
It may be required to override the logic to select the right service manager. For example, to check a service managed by Upstart.
|
||||||
|
|
||||||
describe upstart_service('service') do
|
```ruby
|
||||||
|
describe upstart_service('SERVICE') do
|
||||||
it { should_not be_enabled }
|
it { should_not be_enabled }
|
||||||
it { should be_installed }
|
it { should be_installed }
|
||||||
it { should be_running }
|
it { should be_running }
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
This is also possible with `systemd_service`, `runit_service`, `sysv_service`, `bsd_service`, and `launchd_service`. Provide the control command when it is not to be found at the default location. For example, if the `sv` command for services managed by runit is not in the `PATH`:
|
This is also possible with `systemd_service`, `runit_service`, `sysv_service`, `bsd_service`, and `launchd_service`. If not found at the default location, provide the **control** command. For example, if the `sv` command for services managed by `runit` is not in the `PATH`.
|
||||||
|
|
||||||
describe runit_service('service', '/opt/chef/embedded/sbin/sv') do
|
```ruby
|
||||||
|
describe runit_service('SERVICE', '/opt/chef/embedded/sbin/sv') do
|
||||||
it { should be_enabled }
|
it { should be_enabled }
|
||||||
it { should be_installed }
|
it { should be_installed }
|
||||||
it { should be_running }
|
it { should be_running }
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Verify that IIS is running
|
### Verify IIS is running
|
||||||
|
|
||||||
|
```ruby
|
||||||
describe service('W3SVC') do
|
describe service('W3SVC') do
|
||||||
it { should be_installed }
|
it { should be_installed }
|
||||||
it { should be_running }
|
it { should be_running }
|
||||||
end
|
end
|
||||||
|
```
|
||||||
|
|
||||||
## Matchers
|
## Matchers
|
||||||
|
|
||||||
For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
|
For a full list of available matchers, please visit the [matchers page](/inspec/matchers/).
|
||||||
|
|
||||||
### be_enabled
|
### be_enabled
|
||||||
|
|
||||||
The `be_enabled` matcher tests if the named service is enabled:
|
The `be_enabled` matcher tests if the named service is enabled:
|
||||||
|
|
||||||
|
```ruby
|
||||||
it { should be_enabled }
|
it { should be_enabled }
|
||||||
|
```
|
||||||
|
|
||||||
### be_installed
|
### be_installed
|
||||||
|
|
||||||
The `be_installed` matcher tests if the named service is installed:
|
The `be_installed` matcher tests if the named service is installed.
|
||||||
|
|
||||||
|
```ruby
|
||||||
it { should be_installed }
|
it { should be_installed }
|
||||||
|
```
|
||||||
|
|
||||||
### be_running
|
### be_running
|
||||||
|
|
||||||
The `be_running` matcher tests if the named service is running:
|
The `be_running` matcher tests if the named service is running.
|
||||||
|
|
||||||
|
```ruby
|
||||||
it { should be_running }
|
it { should be_running }
|
||||||
|
```
|
||||||
|
|
||||||
|
### be_monitored_by
|
||||||
|
|
||||||
|
The `be_monitored_by` matcher accepts the name of a monitoring tool as input and test if the named service is monitored respectively. The monitoring tool supports `monit` and `god` resources.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
it { should be_monitored_by("god") }
|
||||||
|
```
|
||||||
|
|
||||||
|
### have_start_mode
|
||||||
|
|
||||||
|
The `have_start_mode` matcher tests accept a mode as input and test if the named service's start mode is the same as specified in the input. This matcher is supported on the Windows systems only.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
it { should have_start_mode('Manual') }
|
||||||
|
```
|
||||||
|
|
|
@ -271,6 +271,30 @@ module Inspec::Resources
|
||||||
info[:startname]
|
info[:startname]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# matcher equivalent to startmode property; compares start-up mode
|
||||||
|
# supported only on windows.
|
||||||
|
def has_start_mode?(mode)
|
||||||
|
raise Inspec::Exceptions::ResourceSkipped, "The `has_start_mode` matcher is not supported on your OS yet." unless inspec.os.windows?
|
||||||
|
|
||||||
|
mode == startmode
|
||||||
|
end
|
||||||
|
|
||||||
|
# matcher to check if the service is monitored by the given monitoring tool/software
|
||||||
|
def monitored_by?(monitoring_tool)
|
||||||
|
# Currently supported monitoring tools are: monit & god
|
||||||
|
# To add support for new monitoring tools, extend the case statement with additional monitoring tool and
|
||||||
|
# add the definition and logic in a new class (inheriting the base class MonitoringTool: optional)
|
||||||
|
case monitoring_tool
|
||||||
|
when "monit"
|
||||||
|
current_monitoring_tool = Monit.new(inspec, @service_name)
|
||||||
|
when "god"
|
||||||
|
current_monitoring_tool = God.new(inspec, @service_name)
|
||||||
|
else
|
||||||
|
puts "The monitoring tool #{monitoring_tool} is not yet supported by InSpec."
|
||||||
|
end
|
||||||
|
current_monitoring_tool.is_service_monitored?
|
||||||
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
"Service #{@service_name}"
|
"Service #{@service_name}"
|
||||||
end
|
end
|
||||||
|
@ -893,4 +917,53 @@ module Inspec::Resources
|
||||||
Runit.new(inspec, service_ctl)
|
Runit.new(inspec, service_ctl)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Helper class for monitored_by matcher
|
||||||
|
class MonitoringTool
|
||||||
|
attr_reader :inspec, :service_name
|
||||||
|
def initialize(inspec, service_name)
|
||||||
|
@inspec = inspec
|
||||||
|
@service_name ||= service_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_utility_or_error(utility_name)
|
||||||
|
[ "/usr/sbin/#{utility_name}" , "/sbin/#{utility_name}" , "/usr/bin/#{utility_name}" , "/bin/#{utility_name}" , "#{utility_name}" ].each do |cmd|
|
||||||
|
return cmd if inspec.command(cmd).exist?
|
||||||
|
end
|
||||||
|
|
||||||
|
raise Inspec::Exceptions::ResourceFailed, "Could not find `#{utility_name}`"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Monit < MonitoringTool
|
||||||
|
def is_service_monitored?
|
||||||
|
utility = find_utility_or_error("monit")
|
||||||
|
utility_cmd = inspec.command("#{utility} summary")
|
||||||
|
|
||||||
|
raise Inspec::Exceptions::ResourceFailed, "Executing #{utility} summary failed: #{utility_cmd.stderr}" if utility_cmd.exit_status.to_i != 0
|
||||||
|
|
||||||
|
monitoring_info = utility_cmd.stdout.split("\n")
|
||||||
|
monitoring_info.map! { |info| info.strip.squeeze(" ") }
|
||||||
|
is_monitored = false
|
||||||
|
monitoring_info.each do |info|
|
||||||
|
if info =~ /^#{service_name} OK.*/
|
||||||
|
is_monitored = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
is_monitored
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class God < MonitoringTool
|
||||||
|
def is_service_monitored?
|
||||||
|
utility = find_utility_or_error("god")
|
||||||
|
utility_cmd = inspec.command("#{utility} status #{service_name}")
|
||||||
|
|
||||||
|
raise Inspec::Exceptions::ResourceFailed, "Executing #{utility} status #{service_name} failed: #{utility_cmd.stderr}" if utility_cmd.exit_status.to_i != 0
|
||||||
|
|
||||||
|
monitoring_info = utility_cmd.stdout.strip
|
||||||
|
monitoring_info =~ /^#{service_name}: up/
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
4
test/fixtures/cmd/monit-summary
vendored
Normal file
4
test/fixtures/cmd/monit-summary
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
Monit 5.26.0 uptime: 3d 17h 59m
|
||||||
|
Service Name Status Type
|
||||||
|
ip-172-31-83-240 OK System
|
||||||
|
ssh OK Process
|
|
@ -316,6 +316,9 @@ class MockLoader
|
||||||
"service sendmail onestatus" => cmd.call("service-sendmail-onestatus"),
|
"service sendmail onestatus" => cmd.call("service-sendmail-onestatus"),
|
||||||
# mock for FreeBSD10Init info
|
# mock for FreeBSD10Init info
|
||||||
"service -l" => cmd.call("service-l"),
|
"service -l" => cmd.call("service-l"),
|
||||||
|
# service mock for monit
|
||||||
|
"monit summary" => cmd.call("monit-summary"),
|
||||||
|
%{sh -c 'type "monit"'} => empty.call,
|
||||||
# services for system 5 e.g. centos6, debian 6
|
# services for system 5 e.g. centos6, debian 6
|
||||||
"service sshd status" => cmd.call("service-sshd-status"),
|
"service sshd status" => cmd.call("service-sshd-status"),
|
||||||
'find /etc/rc*.d /etc/init.d/rc*.d -name "S*"' => cmd.call("find-etc-rc-d-name-S"),
|
'find /etc/rc*.d /etc/init.d/rc*.d -name "S*"' => cmd.call("find-etc-rc-d-name-S"),
|
||||||
|
|
|
@ -544,5 +544,21 @@ describe "Inspec::Resources::Service" do
|
||||||
it "checks disabled false if some services are not disabled" do
|
it "checks disabled false if some services are not disabled" do
|
||||||
_(service.runlevels(0, 4).enabled?).must_equal false
|
_(service.runlevels(0, 4).enabled?).must_equal false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# windows
|
||||||
|
it "verify serverspec compatible matchers on windows" do
|
||||||
|
resource = MockLoader.new(:windows).load_resource("service", "dhcp")
|
||||||
|
_(resource.name).must_equal "dhcp"
|
||||||
|
_(resource.has_start_mode?("Auto")).must_equal true
|
||||||
|
end
|
||||||
|
|
||||||
|
# ubuntu
|
||||||
|
it "verify serverspec compatible matchers on ubuntu" do
|
||||||
|
resource = MockLoader.new(:ubuntu1404).load_resource("service", "ssh")
|
||||||
|
_(resource.name).must_equal "ssh"
|
||||||
|
_(resource.monitored_by?("monit")).must_equal true
|
||||||
|
ex = _ { resource.has_start_mode?("Auto") }.must_raise(Inspec::Exceptions::ResourceSkipped)
|
||||||
|
_(ex.message).must_include "The `has_start_mode` matcher is not supported on your OS yet."
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue