mirror of
https://github.com/inspec/inspec
synced 2024-11-10 15:14:23 +00:00
Merge pull request #5935 from inspec/ss/resource-cgroup
CFINSPEC-73: Add cgroup resource
This commit is contained in:
commit
03d0f2374b
7 changed files with 401 additions and 0 deletions
79
docs-chef-io/content/inspec/resources/cgroup.md
Normal file
79
docs-chef-io/content/inspec/resources/cgroup.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
+++
|
||||
title = "cgroup resource"
|
||||
draft = false
|
||||
gh_repo = "inspec"
|
||||
platform = "linux"
|
||||
|
||||
[menu]
|
||||
[menu.inspec]
|
||||
title = "cgroup"
|
||||
identifier = "inspec/resources/os/cgroup.md cgroup resource"
|
||||
parent = "inspec/resources/os"
|
||||
+++
|
||||
|
||||
Use the `cgroup` Chef InSpec audit resource to test the different parameters values of the control group (cgroup) resource controllers. A cgroup is a Linux kernel feature that limits, accounts, and isolates the resource usage (such as CPU, memory, disk I/O, network) of a collection of processes.
|
||||
|
||||
## Availability
|
||||
|
||||
### Installation
|
||||
|
||||
This resource is distributed with Chef InSpec.
|
||||
|
||||
## Syntax
|
||||
|
||||
describe cgroup("CARROTKING") do
|
||||
its("cpuset.cpus") { should eq 0 }
|
||||
end
|
||||
where
|
||||
|
||||
- `cpuset.cpus` is a property of this resource and a parameter of the *cpuset* resource controller.
|
||||
- `CARROTKING` is the name of cgroup directory.
|
||||
|
||||
## Properties
|
||||
|
||||
- All parameters of the cgroup resource controller are valid properties of this resource. Some of them are: `cpuset.cpus`, `memory.limit_in_bytes`, `memory.stat`, `freezer.state`, `cpu.stat`, `cpuacct.usage`, `pids.current`, `blkio.throttle.io_service_bytes`.
|
||||
|
||||
## Matchers
|
||||
|
||||
- For a full list of available matchers, refer [matchers page](https://docs.chef.io/inspec/matchers/).
|
||||
- The matchers applicable for this resource are: `eq`, `cmp`, and `match`.
|
||||
|
||||
### eq
|
||||
|
||||
`eq` tests whether the two values are of same data type and includes configuration entries that are numbers. It fails if the types do not match. Use `cmp` for less restrictive comparisons that ignores data type while comparing.
|
||||
|
||||
### cmp
|
||||
|
||||
Unlike `eq`, `cmp` is a matcher for less-restrictive comparisons. This matcher attempts to fit the actual value to the comparing type and meant to relieve the user from having to write type-casts and resolutions.
|
||||
|
||||
### match
|
||||
|
||||
`match` checks if a string matches a regular expression. Use `match` when the output of `cgget -n -r [subsystem.parameters] [cgroup-name]` is a multi-line output.
|
||||
|
||||
## Examples
|
||||
|
||||
The following examples show how to use this Chef InSpec audit resource.
|
||||
|
||||
### Example 1
|
||||
|
||||
Use `eq` to test for parameters that have a single line integer value. The value considered is the output obtained on `cgget -n -r [subsystem.parameters] [cgroup-name]`.
|
||||
|
||||
describe cgroup("CARROTKING") do
|
||||
its("cpuset.cpus") { should eq 0 }
|
||||
end
|
||||
|
||||
### Example 2
|
||||
|
||||
Use `cmp` to test for parameters with less-restrictive comparisons and has a single line integer value. The value considered is the output obtained on `cgget -n -r [subsystem.parameters] [cgroup-name]`.
|
||||
|
||||
describe cgroup("CARROTKING") do
|
||||
its("memory.limit_in_bytes") { should cmp 9223372036854771712 }
|
||||
end
|
||||
|
||||
### Example 3
|
||||
|
||||
Use `match` to test for parameters that have multi-line values and can be passed as *regex*. The value considered is the output obtained on `cgget -n -r [subsystem.parameters] [cgroup-name]`.
|
||||
|
||||
describe cgroup("CARROTKING") do
|
||||
its("memory.stat") { should match /\bhierarchical_memory_limit 9223372036854771712\b/ }
|
||||
end
|
101
lib/inspec/resources/cgroup.rb
Normal file
101
lib/inspec/resources/cgroup.rb
Normal file
|
@ -0,0 +1,101 @@
|
|||
require "inspec/resources/command"
|
||||
module Inspec::Resources
|
||||
class Cgroup < Inspec.resource(1)
|
||||
name "cgroup"
|
||||
# Restrict to only run on the below platform
|
||||
supports platform: "linux"
|
||||
desc "Use the cgroup InSpec audit resource to test cgroup subsytem's parameters."
|
||||
|
||||
example <<~EXAMPLE
|
||||
describe cgroup("foo") do
|
||||
its("cpuset.cpus") { should eq 0 }
|
||||
its("memory.limit_in_bytes") { should eq 499712 }
|
||||
its("memory.limit_in_bytes") { should be <= 500000 }
|
||||
its("memory.numa_stat") { should match /total=0/ }
|
||||
end
|
||||
EXAMPLE
|
||||
|
||||
# Resource initialization.
|
||||
def initialize(cgroup_name)
|
||||
raise Inspec::Exceptions::ResourceSkipped, "The `cgroup` resource is not supported on your OS yet." unless inspec.os.linux?
|
||||
|
||||
@cgroup_name = cgroup_name
|
||||
@valid_queries, @valid_queries_split = [], []
|
||||
find_valid_queries
|
||||
# Used to track the method calls in an "its" query
|
||||
@cgroup_info_query = []
|
||||
end
|
||||
|
||||
def resource_id
|
||||
@cgroup_name
|
||||
end
|
||||
|
||||
def to_s
|
||||
"cgroup #{resource_id}"
|
||||
end
|
||||
|
||||
def method_missing(param)
|
||||
# Add the latest param we've seen to the list and form the query with all the params we've seen so far.
|
||||
@cgroup_info_query << param.to_s
|
||||
query = @cgroup_info_query.join(".")
|
||||
|
||||
# The ith level param must match with atleast one row's ith column of @valid_queries_split
|
||||
# Else there is no way, we would find any valid query in further iteration, so raise exception.
|
||||
if @valid_queries_split.map { |e| e[@cgroup_info_query.length - 1] }.include?(param.to_s)
|
||||
# If the query form so far is part of @valid_queries, we are good to trigger find_cgroup_info
|
||||
# else go for next level of param
|
||||
if @valid_queries.include?(query)
|
||||
@cgroup_info_query = []
|
||||
find_cgroup_info(query)
|
||||
else
|
||||
self
|
||||
end
|
||||
else
|
||||
@cgroup_info_query = []
|
||||
|
||||
raise Inspec::Exceptions::ResourceFailed, "The query #{query} does not appear to be valid."
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Method to find cgget tool
|
||||
def find_cgget_or_error
|
||||
%w{/usr/sbin/cgget /sbin/cgget cgget}.each do |cmd|
|
||||
return cmd if inspec.command(cmd).exist?
|
||||
end
|
||||
|
||||
raise Inspec::Exceptions::ResourceFailed, "Could not find `cgget`"
|
||||
end
|
||||
|
||||
# find the cgroup info of the query which is given as input by the user
|
||||
def find_cgroup_info(query)
|
||||
bin = find_cgget_or_error
|
||||
cgget_cmd = "#{bin} -n -r #{query} #{@cgroup_name}"
|
||||
cmd = inspec.command(cgget_cmd)
|
||||
|
||||
raise Inspec::Exceptions::ResourceFailed, "Executing cgget failed: #{cmd.stderr}" if cmd.exit_status.to_i != 0
|
||||
|
||||
# For complex returns the user must use match /the_regex/
|
||||
param_value = cmd.stdout.split(":")
|
||||
return nil if param_value.nil? || param_value.empty?
|
||||
|
||||
param_value = param_value[1].strip.split("\t").join
|
||||
param_value.match(/^\d+$/) ? param_value.to_i : param_value
|
||||
end
|
||||
|
||||
# find all the information about all relevant controllers for the current cgroup
|
||||
def find_valid_queries
|
||||
bin = find_cgget_or_error
|
||||
cgget_all_cmd = "#{bin} -n -a #{@cgroup_name}"
|
||||
cmd = inspec.command(cgget_all_cmd)
|
||||
|
||||
raise Inspec::Exceptions::ResourceFailed, "Executing cgget failed: #{cmd.stderr}" if cmd.exit_status.to_i != 0
|
||||
|
||||
queries = cmd.stdout.to_s.gsub(/:.*/, "").gsub(/^\s+.*/, "").split("\n")
|
||||
# store the relevant controller parameters in @valid_queries and the dot splitted paramters into @valid_queries_split
|
||||
@valid_queries = queries.map { |q| q if q.length > 0 }.compact
|
||||
@valid_queries_split = @valid_queries.map { |q| q.split(".") }.compact
|
||||
end
|
||||
end
|
||||
end
|
146
test/fixtures/cmd/cgget-n-a
vendored
Normal file
146
test/fixtures/cmd/cgget-n-a
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
devices.list: a *:* rwm
|
||||
cpuset.memory_pressure: 0
|
||||
cpuset.memory_migrate: 0
|
||||
cpuset.mem_exclusive: 0
|
||||
cpuset.memory_spread_slab: 0
|
||||
cpuset.cpu_exclusive: 0
|
||||
cpuset.effective_mems: 0
|
||||
cpuset.effective_cpus: 0
|
||||
cpuset.sched_load_balance: 1
|
||||
cpuset.mems: 0
|
||||
cpuset.mem_hardwall: 0
|
||||
cpuset.sched_relax_domain_level: -1
|
||||
cpuset.cpus: 0
|
||||
cpuset.memory_spread_page: 0
|
||||
freezer.self_freezing: 0
|
||||
freezer.parent_freezing: 0
|
||||
freezer.state: THAWED
|
||||
hugetlb.2MB.limit_in_bytes: 9223372036852678656
|
||||
hugetlb.2MB.usage_in_bytes: 0
|
||||
hugetlb.2MB.rsvd.failcnt: 0
|
||||
hugetlb.2MB.max_usage_in_bytes: 0
|
||||
hugetlb.2MB.rsvd.limit_in_bytes: 9223372036852678656
|
||||
hugetlb.2MB.rsvd.usage_in_bytes: 0
|
||||
hugetlb.2MB.rsvd.max_usage_in_bytes: 0
|
||||
hugetlb.2MB.failcnt: 0
|
||||
net_cls.classid: 0
|
||||
net_prio.prioidx: 7
|
||||
net_prio.ifpriomap: lo 0
|
||||
eth0 0
|
||||
lxdbr0 0
|
||||
vethb7b9a0f5 0
|
||||
lxcbr0 0
|
||||
veth72d3ce6b 0
|
||||
blkio.throttle.read_iops_device:
|
||||
blkio.throttle.io_service_bytes: Total 0
|
||||
blkio.throttle.write_iops_device:
|
||||
blkio.throttle.read_bps_device:
|
||||
blkio.throttle.write_bps_device:
|
||||
blkio.throttle.io_serviced: Total 0
|
||||
blkio.throttle.io_service_bytes_recursive: Total 0
|
||||
blkio.throttle.io_serviced_recursive: Total 0
|
||||
rdma.current:
|
||||
rdma.max:
|
||||
cpu.cfs_period_us: 100000
|
||||
cpu.stat: nr_periods 0
|
||||
nr_throttled 0
|
||||
throttled_time 0
|
||||
cpu.shares: 1024
|
||||
cpu.cfs_quota_us: -1
|
||||
cpu.uclamp.min: 0.00
|
||||
cpu.uclamp.max: max
|
||||
cpuacct.usage_percpu_sys: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
cpuacct.usage_percpu: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
cpuacct.stat: user 0
|
||||
system 0
|
||||
cpuacct.usage: 0
|
||||
cpuacct.usage_sys: 0
|
||||
cpuacct.usage_all: cpu user system
|
||||
0 0 0
|
||||
1 0 0
|
||||
2 0 0
|
||||
3 0 0
|
||||
4 0 0
|
||||
5 0 0
|
||||
6 0 0
|
||||
7 0 0
|
||||
8 0 0
|
||||
9 0 0
|
||||
10 0 0
|
||||
11 0 0
|
||||
12 0 0
|
||||
13 0 0
|
||||
14 0 0
|
||||
cpuacct.usage_percpu_user: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
|
||||
cpuacct.usage_user: 0
|
||||
memory.use_hierarchy: 1
|
||||
memory.kmem.tcp.usage_in_bytes: 0
|
||||
memory.soft_limit_in_bytes: 9223372036854771712
|
||||
memory.move_charge_at_immigrate: 0
|
||||
memory.memsw.failcnt: 0
|
||||
memory.kmem.tcp.max_usage_in_bytes: 0
|
||||
memory.max_usage_in_bytes: 0
|
||||
memory.oom_control: oom_kill_disable 0
|
||||
under_oom 0
|
||||
oom_kill 0
|
||||
memory.stat: cache 0
|
||||
rss 0
|
||||
rss_huge 0
|
||||
shmem 0
|
||||
mapped_file 0
|
||||
dirty 0
|
||||
writeback 0
|
||||
swap 0
|
||||
pgpgin 0
|
||||
pgpgout 0
|
||||
pgfault 0
|
||||
pgmajfault 0
|
||||
inactive_anon 0
|
||||
active_anon 0
|
||||
inactive_file 0
|
||||
active_file 0
|
||||
unevictable 0
|
||||
hierarchical_memory_limit 9223372036854771712
|
||||
hierarchical_memsw_limit 9223372036854771712
|
||||
total_cache 0
|
||||
total_rss 0
|
||||
total_rss_huge 0
|
||||
total_shmem 0
|
||||
total_mapped_file 0
|
||||
total_dirty 0
|
||||
total_writeback 0
|
||||
total_swap 0
|
||||
total_pgpgin 0
|
||||
total_pgpgout 0
|
||||
total_pgfault 0
|
||||
total_pgmajfault 0
|
||||
total_inactive_anon 0
|
||||
total_active_anon 0
|
||||
total_inactive_file 0
|
||||
total_active_file 0
|
||||
total_unevictable 0
|
||||
memory.kmem.slabinfo:
|
||||
memory.limit_in_bytes: 9223372036854771712
|
||||
memory.swappiness: 60
|
||||
memory.memsw.max_usage_in_bytes: 0
|
||||
memory.numa_stat: total=0 N0=0
|
||||
file=0 N0=0
|
||||
anon=0 N0=0
|
||||
unevictable=0 N0=0
|
||||
hierarchical_total=0 N0=0
|
||||
hierarchical_file=0 N0=0
|
||||
hierarchical_anon=0 N0=0
|
||||
hierarchical_unevictable=0 N0=0
|
||||
memory.kmem.failcnt: 0
|
||||
memory.kmem.max_usage_in_bytes: 0
|
||||
memory.usage_in_bytes: 0
|
||||
memory.memsw.limit_in_bytes: 9223372036854771712
|
||||
memory.failcnt: 0
|
||||
memory.kmem.tcp.failcnt: 0
|
||||
memory.kmem.limit_in_bytes: 9223372036854771712
|
||||
memory.memsw.usage_in_bytes: 0
|
||||
memory.kmem.usage_in_bytes: 0
|
||||
memory.kmem.tcp.limit_in_bytes: 9223372036854771712
|
||||
pids.current: 0
|
||||
pids.events: max 0
|
||||
pids.max: max
|
1
test/fixtures/cmd/cgget-n-r
vendored
Normal file
1
test/fixtures/cmd/cgget-n-r
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
cpuset.cpus: 0
|
36
test/fixtures/cmd/cgget-n-r-stat
vendored
Normal file
36
test/fixtures/cmd/cgget-n-r-stat
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
memory.stat: cache 0
|
||||
rss 0
|
||||
rss_huge 0
|
||||
shmem 0
|
||||
mapped_file 0
|
||||
dirty 0
|
||||
writeback 0
|
||||
swap 0
|
||||
pgpgin 0
|
||||
pgpgout 0
|
||||
pgfault 0
|
||||
pgmajfault 0
|
||||
inactive_anon 0
|
||||
active_anon 0
|
||||
inactive_file 0
|
||||
active_file 0
|
||||
unevictable 0
|
||||
hierarchical_memory_limit 9223372036854771712
|
||||
hierarchical_memsw_limit 9223372036854771712
|
||||
total_cache 0
|
||||
total_rss 0
|
||||
total_rss_huge 0
|
||||
total_shmem 0
|
||||
total_mapped_file 0
|
||||
total_dirty 0
|
||||
total_writeback 0
|
||||
total_swap 0
|
||||
total_pgpgin 0
|
||||
total_pgpgout 0
|
||||
total_pgfault 0
|
||||
total_pgmajfault 0
|
||||
total_inactive_anon 0
|
||||
total_active_anon 0
|
||||
total_inactive_file 0
|
||||
total_active_file 0
|
||||
total_unevictable 0
|
|
@ -378,6 +378,11 @@ class MockLoader
|
|||
# lxc
|
||||
"/usr/sbin/lxc info my-ubuntu-container | grep -i Status" => cmd.call("lxcinfo"),
|
||||
%{sh -c 'type "/usr/sbin/lxc"'} => empty.call,
|
||||
# cgroup
|
||||
"cgget -n -a carrotking" => cmd.call("cgget-n-a"),
|
||||
"cgget -n -r cpuset.cpus carrotking" => cmd.call("cgget-n-r"),
|
||||
"cgget -n -r memory.stat carrotking" => cmd.call("cgget-n-r-stat"),
|
||||
%{sh -c 'type "cgget"'} => empty.call,
|
||||
# apache_conf
|
||||
"sh -c 'find /etc/apache2/ports.conf -type f -maxdepth 1'" => cmd.call("find-apache2-ports-conf"),
|
||||
"sh -c 'find /etc/httpd/conf.d/*.conf -type f -maxdepth 1'" => cmd.call("find-httpd-ssl-conf"),
|
||||
|
|
33
test/unit/resources/cgroup_test.rb
Normal file
33
test/unit/resources/cgroup_test.rb
Normal file
|
@ -0,0 +1,33 @@
|
|||
require "inspec/globals"
|
||||
require "#{Inspec.src_root}/test/helper"
|
||||
require_relative "../../../lib/inspec/resources/cgroup"
|
||||
|
||||
describe Inspec::Resources::Cgroup do
|
||||
# ubuntu
|
||||
it "check carrotking cgroup information on ubuntu" do
|
||||
resource = MockLoader.new("ubuntu".to_sym).load_resource("cgroup", "carrotking")
|
||||
_(resource.cpuset.cpus).must_equal 0
|
||||
_(resource.memory.stat).must_match(/hierarchical_memory_limit 9223372036854771712/)
|
||||
end
|
||||
|
||||
# debian
|
||||
it "check carrotking cgroup information on debian" do
|
||||
resource = MockLoader.new("debian8".to_sym).load_resource("cgroup", "carrotking")
|
||||
_(resource.cpuset.cpus).must_equal 0
|
||||
_(resource.memory.stat).must_match(/hierarchical_memory_limit 9223372036854771712/)
|
||||
end
|
||||
|
||||
# windows
|
||||
it "check carrotking cgroup information on windows" do
|
||||
resource = MockLoader.new("windows".to_sym).load_resource("cgroup", "carrotking")
|
||||
_(resource.resource_skipped?).must_equal true
|
||||
_(resource.resource_exception_message).must_equal "The `cgroup` resource is not supported on your OS yet."
|
||||
end
|
||||
|
||||
# undefined
|
||||
it "check carrotking cgroup information on unsupported os" do
|
||||
resource = MockLoader.new("undefined".to_sym).load_resource("cgroup", "carrotking")
|
||||
_(resource.resource_skipped?).must_equal true
|
||||
_(resource.resource_exception_message).must_equal "The `cgroup` resource is not supported on your OS yet."
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue