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"
|
||||
+++
|
||||
|
||||
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
|
||||
|
||||
### Installation
|
||||
|
||||
This resource is distributed along with Chef InSpec itself. You can use it automatically.
|
||||
The Chef InSpec distributes this resource.
|
||||
|
||||
### Version
|
||||
|
||||
This resource first became available in v1.0.0 of InSpec.
|
||||
This resource is available from Chef Inspec version 1.0.0.
|
||||
|
||||
## 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_enabled }
|
||||
it { should be_running }
|
||||
end
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
- `('service_name')` must specify a service name
|
||||
- `be_installed`, `be_enabled`, and `be_running` are valid matchers for this resource
|
||||
> where
|
||||
>
|
||||
> - `('service_name')` must specify a service name
|
||||
> - `be_installed`, `be_enabled`, and `be_running` are valid matchers for this resource
|
||||
|
||||
## Examples
|
||||
|
||||
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_running }
|
||||
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_running }
|
||||
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
|
||||
it { should be_installed }
|
||||
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_running }
|
||||
end
|
||||
```
|
||||
|
||||
### Test Unix SystemV run levels
|
||||
|
||||
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) }
|
||||
end
|
||||
|
||||
describe service('sshd').runlevels(2,4) do
|
||||
describe service('SSH').runlevels(2,4) do
|
||||
it { should be_enabled }
|
||||
end
|
||||
```
|
||||
|
||||
### 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 be_installed }
|
||||
it { should be_running }
|
||||
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_installed }
|
||||
it { should be_running }
|
||||
end
|
||||
```
|
||||
|
||||
### Verify that IIS is running
|
||||
### Verify IIS is running
|
||||
|
||||
```ruby
|
||||
describe service('W3SVC') do
|
||||
it { should be_installed }
|
||||
it { should be_running }
|
||||
end
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
The `be_enabled` matcher tests if the named service is enabled:
|
||||
|
||||
```ruby
|
||||
it { should be_enabled }
|
||||
```
|
||||
|
||||
### 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 }
|
||||
```
|
||||
|
||||
### 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 }
|
||||
```
|
||||
|
||||
### 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]
|
||||
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
|
||||
"Service #{@service_name}"
|
||||
end
|
||||
|
@ -893,4 +917,53 @@ module Inspec::Resources
|
|||
Runit.new(inspec, service_ctl)
|
||||
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
|
||||
|
|
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"),
|
||||
# mock for FreeBSD10Init info
|
||||
"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
|
||||
"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"),
|
||||
|
|
|
@ -544,5 +544,21 @@ describe "Inspec::Resources::Service" do
|
|||
it "checks disabled false if some services are not disabled" do
|
||||
_(service.runlevels(0, 4).enabled?).must_equal false
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue