Initialize a serverspec directory with boilerplate
serverspec-init
should
means "assert true".should_not
means "assert false".
http://serverspec.org/resource_types.html
Files, directories, and devices.
describe file('/path/to/file') do
#INode type
it { should be_file } # or be_directory, or be_symlink
it { should exist }
#Permissions
it { should be_owned_by 'username' }
it { should be_grouped_into 'groupname' }
it { should be_mode 440 }
it { should be_readable } # or be_writable or be_executable
it { should be_readable.by('owner') } # or 'group' or 'others'
it { should be_readable.by_user('username') }
#Links
if { should be_linked_to '/path/to/target' }
#Contents
its(:md5sum) { should eq '...' } # or, and rspec matcher
its(:sha256sum) { should eq '...' }
its(:size) { should < 1024 }
#should match works like contains-you can use a regex contained within single quotes
#this also works for validating command output
its(:content) { should match /some pattern/ }
end
System users.
describe user('username') do
it { should exist }
it { should belong_to_group 'group' }
it { should have_home_directory '/home/username' }
it { should have_login_shell '/bin/bash' }
it { should have_authorized_key 'ssh-rsa ABCD... user@hostname' }
end
System user groups.
describe group('groupname') do
it { should exist }
end
Software packages installed on the system.
describe package('httpd') do
it { should be_installed }
end
List of software packages that should not be installed
removed_package_list = %w(
telnet
vim
)
removed_package_list.each do |package|
describe package(package) do
it { should_not be_installed }
end
end
Select based on operating system:
describe package('httpd'), :if => os[:family] == 'redhat' do
it { should be_installed }
end
describe package('apache2'), :if => os[:family] == 'ubuntu' do
it { should be_installed }
end
Network ports
describe port(80) do
it { should be_listening }
end
Installed services.
describe service('httpd') do
it { should be_running }
it { should be_enabled } # enabled to start when the OS boots.
end
Currently running processes.
describe process("memchached") do
it { should be_running }
# parameters from ps, see `man ps(1)`, under "STANDARD FORMAT SPECIFIERS"
its(:user) { should eq 'root' }
its(:args) { should match /-c 32000\b/ }
its(:nice) { should > 10 }
end
Run arbitrary commands and check the results.
describe command('ls -al /') do
its(:stdout) { should match /some pattern/ }
its(:stderr) { should be_empty }
its(:exit_status) { should eq 0 }
end
Hosts on the network
describe host('example.org') do
it { should be_resolvable }
# address
its(:ipaddress) { should match /192\.168\.10\.10/ } # could be IPv4 or IPv6
its(:ipv4_address) { ... }
its(:ipv6_address) { ... }
# reachability
it { should be_reachable } # ping
it { should be_reachable.with(
:port => 53, # required parameter
# Optional params (default values shown)
:proto => 'tcp', # or 'udp'
:timeout => 5 # in seconds.
)}
end
https://www.relishapp.com/rspec/rspec-expectations/v/3-4/docs/built-in-matchers
To try these outside of a serverspec test, you'll need to require 'RSpec'
,
and replace describe
with RSpec.describe
.
describe 'foobar' do
it { should eq 'foobar' } # match using == operator
it { should match /ooba/ } # match using regex, anywhere in string.
it { should_not match /^ooba$/ } # anchor regex
it { should_not be_empty } # test for empty string: ""
it { should start_with('fo') }
it { should end_with('bar') }
it { should be_a(String) }
end
describe 10 do
it { should eq 10 }
it { should == 10 } # same as above.
it { should < 20 }
it { should <= 10 }
it { should > 0 }
it { should >= 9 }
it { should be_within(2).of(9) }
it { should be_within(2).of(11) }
it { should be_a(Numeric) } # also consider: Float, Integer
it { should be_an_instance_of(Fixnum) } # Direct class, no higher
end
describe [1, 2, 3] do
it { should_not be_empty } # test for empty list: []
it { should include(2) } # membership test
it { should_not include(0) }
it { should all( be_an(Integer) ) } # apply matcher to all elements
it { should all( be_an(Integer).and be < 10 ) } # conjunction
it { should start_with([1,2]) }
it { should end_with([2,3]) }
it { should be_an(Array) }
end
describe ({ :a => 'A', :b => 2 }) do
it { should have_key(:a) }
it { should include(:a) } # same as above
it { should include(:b => 2) } # test for presence of key and the value it maps to.
it { should_not include(:b => 3) }
it { should_not include(:c => 2) }
it { should be_a(Hash) }
end
describe(3.14159) do
# Passes as long as the block returns true.
it { should satisfy { |uut|
uut.kind_of?(Numeric) and uut > 3 and uut < 3.2
}}
end
describe("thunder") do
it { should start_with("thun").and end_with("der") }
it { should start_with("won").or start_with("thun") }
it { should (start_with("won").or start_with("thun")).and end_with("der") }
# with line breaks
it { should (
start_with("won").or \
start_with("thun").or \
start_with("pon")
).and (
end_with("der")
)
}
end
RSpec::Matchers.define :be_multiple_of do |expected|
match do |actual|
actual % expected == 0
end
# optional, override description
description do
"be multiple of #{expected}"
end
# optionally, override failure message:
failure_message do |actual|
"expected that #{actual} would be a multiple of #{expected}"
end
# optionally, override failure message when negated:
failure_message_when_negated do |actual|
"expected that #{actual} would not be a multiple of #{expected}"
end
end
describe(9) do
it { should be_multiple_of(3) }
#Deliberate failures
it { should be_multiple_of(2) }
it { should_not be_multiple_of(3) }
end