詳細検索

Test recipes written in Chef (serverspec)

Avatar
by komi

Test recipes written in Chef (serverspec)
Translated from 日本語 • View original

Hello. This is Komiya.

This is the last time.
Here are some places to test the recipes I introduced last time. Serverspec.

I wrote in the link below why Serverspec is good, but I think the following points are good.
・It is not a Chef test tool, but an external tool, so there are no dependencies (it can also be used in puppet).
・The design concept is simple and easy to use, so it was easy to use without much effort.

Reference:
Serverspec at hbstudy #45 Introduction Chef Solo Picking up fallen ears kayac/newbie-training "Introductory Puppet" resource_type manual advanced_tipsNCSTUDY#05 Hands-on Materials Parallel_tests

・Setup
Versions 0.3 and 0.6 felt that the way the tests were written was slightly different, so I recommend the new one that follows the manual.
[shell]# yum install rubygems

gem install serverspec rake

serverspec-init

Select a backend type:

1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: y
Input vagrant instance name: 10.0.0.241
 + spec/10.0.0.241/
 + spec/10.0.0.241/httpd_spec.rb[/shell]  
  
  
  
  
  
  
<br>When the command is finished running, it will create some files as described above.<br>```

\[shell\]# serverspec-init ~omitted~ Input target host name: 10.0.0.240 + spec/10.0.0.240/ + spec/10.0.0.240/httpd\_spec.rb\[/shell\]  
After specifying SSH and entering the target host, a directory for each host was created.  
  
I'm going to write a test.  
  
\[shell\]# vi spec/10.0.0.240/httpd\_spec.rb require 'spec\_helper'

describe 'httpd' do it { should be_installed } it { should be_enabled } it { should be_running } end

describe 'port 80' do it { should be_listening } end

describe '/etc/httpd/conf/httpd.conf' do it { should be_file } it { should contain "ServerName localhost:80" } end[/shell]
Changed from DetectOS to RedHat (because it's CentOS)
```

[shell]# vi spec/spec_helper.rb include Serverspec::Helper::RedHat[/shell]
Modify the config of ssh to be able to pass the connection information
[shell]# vi .ssh/config Host 10.0.0.240 HostName 10.0.0.240 User root Port 22 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile "/root/.ssh/ komi-test.pem" IdentitiesOnly yes LogLevel FATAL Host 10.0.0.241 HostName 10.0.0.241 User root Port 22 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentityFile "/root/.ssh/komi-test.pem" IdentitiesOnly yes[/shell]
Modify the test file name on the DB side and modify the content to match the entity
[shell]# mv spec/10.0.0.241/httpd_spec.rb spec/10.0.0.241/mysqld_spec.rb

vi spec/10.0.0.241/mysqld_spec.rb

require 'spec_helper'

describe 'mysql-server' do
  it { should be_installed }
end

describe 'mysqld' do
  it { should be_enabled   }
  it { should be_running   }
end

describe 'port 3306' do
  it { should be_listening }
end

describe '/etc/my.cnf' do
  it { should be_file }
  it { should contain &quot;server-id = 103&quot; }
end[/shell]<br>For the time being, I will check if the test is as written<br>```

\[shell\]# rake spec (in /root) /root/.rbenv/versions/1.9.2-p290/bin/ruby -S rspec spec/10.0.0.240/httpd\_spec.rb spec/10.0.0.241/mysqld\_spec.rb ............

Finished in 0.74866 seconds 12 examples, 0 failures[/shell]
It seems that what I wrote went well.
In the case of an error, the following command that failed and its return are printed, so it is very easy to understand.
```

[shell]Failures:

  1) mysql-server
     Failure/Error: it { should be_enabled   }
       chkconfig --list mysql-server | grep 3:on
       error reading information on service mysql-server: No such file or directory

2) mysql-server
     Failure/Error: it { should be_running   }
       ps aux | grep -w -- mysql-server | grep -qv grep[/shell]<br>→ these are said to be mysql-server in chkconfig and ps commands, so I will fix them by separating the should be_installed, enabled, and running blocks.<br>
<br>
<br>Next, we will add all the necessary tests and test them<br>In order to reuse recipes and tests as a set, it is thought that it would be better to divide the test file into the same way as the recipe.<br>```

For the time being, I will divide the test files into cookbook units.  
Maybe you can create a test file with a name corresponding to the recipe for each host directory.  
Base\_setting recipe tests are duplicated, so I would like to manage them for each role, but this will be an issue in the future.  
  
\[shell\]# vi spec/10.0.0.240/base\_spec.rb require 'spec\_helper'

Create a backup directory

describe file('/etc/.backup') do it { should be_directory } end

describe file('/etc/hosts') do it { should contain '10.0.0.240 xxx-web03' } end

Setting the default gateway

describe default_gateway do its(:ipaddress) { should eq '10.0.0.93' } its(:interface) { should eq 'eth0' } end

selinux is disable

describe selinux do it { should be_disabled } end

Exclude update kernel in yum.conf

describe file('/etc/yum.conf') do it { should contain 'exclude=kernel*' } end

Disable ipv6 in modprobe.conf

describe file('/etc/modprobe.conf') do it { should contain 'options ipv6 disable=1' } end

The required package must be included

%w{ sendmail ntp }.each do |pkg| describe package("#{pkg}") do it { should be_installed } end end

Unnecessary service outage

%w{ ip6tables iptables messagebus kudzu }.each do |services| describe service("services") do it { should_not be_enabled } it { should_not be_running } end end

log cut-off settings

describe file('/etc/logrotate.d/syslog') do it { should contain 'compress' } it { should contain 'rotate 53' } end

Unnecessary cron permission must be 0

%w{ makewhatis.cron mlocate.cron prelink }.each do |files| describe file("/etc/cron.daily/#{files}") do it { should be_mode 0 } end end

describe file('/etc/cron.weekly/makewhatis.cron') do it { should be_mode 0 } end

cron for ntpdate

describe cron do it { should have_entry '0 * * * * /usr/sbin/ntpdate -bs 10.0.0.93' } end

timezone

#describe file('/etc/localtime') do

it { should be_linked_to '/usr/share/zoneinfo/Japan' }

#end

kernelparams for webserver

describe 'Linux kernel parameters' do context linux_kernel_parameter('net.ipv4.tcp_syncookies') do its(:value) { should eq 1 } end

context linux_kernel_parameter('vm.swappiness') do its(:value) { should eq 30 } end

context linux_kernel_parameter('net.ipv4.tcp_tw_reuse') do its(:value) { should eq 0 } end

context linux_kernel_parameter('net.ipv4.tcp_tw_recycle') do its(:value) { should eq 0 } end

context linux_kernel_parameter('net.ipv4.tcp_fin_timeout') do its(:value) { should eq 60 } end

context linux_kernel_parameter('net.ipv4.tcp_max_syn_backlog') do its(:value) { should eq 4096 } end

context linux_kernel_parameter('net.core.somaxconn') do its(:value) { should eq 4096 } end end

Testing login users

%w{ xxx-op yyy-op dev }.each do |u| describe user("#{u}") do it { should exist } it { should belong_to_group 'wheel' } end end


# rake spec

Omitted Finished in 3.13 seconds 40 examples, 0 failures[/shell]
For the time being, I have moved so far.
If you don't upgrade the serverspec itself to 0.6, some linux_kernel_paramater and cron won't work because there is no such method.

```

[shell]# cp -p spec/10.0.0.240/base_spec.rb spec/10.0.0.241/

vi spec/10.0.0.241/base_spec.rb

diff spec/10.0.0.240/base_spec.rb spec/10.0.0.241/base_spec.rb

9c9
&lt;   it { should contain '10.0.0.240     xxx-web03' }
---
&gt;   it { should contain '10.0.0.241     xxx-db03' }
75c75
&lt; # kernelparams for webserver
---
&gt; # kernelparams for dbserver
82c82
&lt;     its(:value) { should eq 30 }
---
&gt;     its(:value) { should eq 0 }
86c86
&lt;     its(:value) { should eq 0 }
---
&gt;     its(:value) { should eq 1 }
90c90
&lt;     its(:value) { should eq 0 }
---
&gt;     its(:value) { should eq 1 }
94c94
&lt;     its(:value) { should eq 60 }
---
&gt;     its(:value) { should eq 10 }
98c98
&lt;     its(:value) { should eq 4096 }
---
&gt;     its(:value) { should eq 8192 }
102c102
&lt;     its(:value) { should eq 4096 }
---
&gt;     its(:value) { should eq 8192 }[/shell]<br>```

・I wrote a little bit of the httpd cookbook test  
\[shell\]require 'spec\_helper'

%w{ httpd php php-pecl-ssh2 php-mysql php-common php-devel php-pear php-pdo php-mbstring php-pecl-apc php-mcrypt php-cli mysql-libs }.each do |pkg| describe package("#{pkg}") do it { should be_installed } end end

describe service('httpd') do it { should be_enabled } it { should be_running } end

describe port(80) do it { should be_listening } end

describe file('/etc/httpd/conf/httpd.conf') do it { should be_file } it { should contain "ServerName localhost:80" } end

basic auth test

describe file('/etc/httpd/conf.d/basic_auth.conf') do it { should be_file } it { should contain "Require valid-user" } end

describe file('/etc/httpd/conf/.htpasswd') do it { should be_file } it { should contain "dev" } end

wp contents exists test

describe file('/var/www/html/wp-config.php') do it { should be_file } end

mount fuse test

#describe file('/var/www/html/assets') do

it { should be_mounted.with(:type => 'fuse') }

it { should be_mounted.with(:options => { :rw => true } ) }

#end[/shell]
```

  • Added some tests for mysqld
    [shell]require 'spec_helper'
describe package('mysql-server') do
  it { should be_installed }
end

describe service('mysqld') do
  it { should be_enabled   }
  it { should be_running   }
end

describe port(3306) do
  it { should be_listening }
end

describe file('/etc/my.cnf') do
  it { should be_file }
  it { should contain &quot;server-id = 103&quot; }
end

describe file('/etc/logrotate.d/mysqld') do
  it { should be_file }
  it { should contain &quot;/usr/bin/mysqladmin flush-logs&quot; }
end

describe file('/opt/bin/mysql-back.sh') do
  it { should be_file }
  it { should be_executable }
end[/shell]<br>・Write a test for munin<br>```

\[shell\]# vi spec/10.0.0.240/munin\_spec.rb require 'spec\_helper'

%w{ munin-node perl-DBI }.each do |pkg| describe package("#{pkg}") do it { should be_installed } end end

describe service('munin-node') do it { should be_enabled } it { should be_running } end

describe port(4949) do it { should be_listening } end

munin plugins

%w{ cpu memory df load tcp iostat if_eth0 if_err_eth0 }.each do |plg| describe file("/etc/munin/plugins/#{plg}") do it { should be_file } end end

munin plugins for httpd

%w{ apache_accesses apache_processes }.each do |plg| describe file("/etc/munin/plugins/#{plg}") do it { should be_file } end end

munin plugins for mysql

#%w{ mysql_slowqueries mysql_queries mysql_threads }.each do |plg|

describe file("/etc/munin/plugins/#{plg}") do

it { should be_file }

end

#end


# cp -p spec/10.0.0.240/munin\_spec.rb spec/10.0.0.241/munin\_spec.rb

# vi spec/10.0.0.241/munin\_spec.rb

# diff spec/10.0.0.240/munin\_spec.rb spec/10.0.0.241/munin\_spec.rb

26,33c26 < %w{ apache_accesses apache_processes }.each do |plg| < describe file("/etc/munin/plugins/#{plg}") do < it { should be_file } < end < end < < ## munin plugins for mysql < #%w{ mysql_slowqueries mysql_queries mysql_threads }.each do |plg|

> #%w{ apache_accesses apache_processes }.each do |plg| 38a32,38 > # munin plugins for mysql > %w{ mysql_slowqueries mysql_queries mysql_threads }.each do |plg| > describe file("/etc/munin/plugins/#{plg}") do > it { should be_file } > end > end >[/shell]
Write a zabbix test
```

[shell]# vi spec/10.0.0.240/zabbix_spec.rb require 'spec_helper'

%w{ zabbix-agent zabbix zabbix-jp-release }.each do |pkg|
  describe package(&quot;#{pkg}&quot;) do
    it { should be_installed }
  end
end

describe service('zabbix-agent') do
  it { should be_enabled }
  it { should be_running }
end

describe port(10050) do
  it { should be_listening }
end

describe file('/opt/bin/mem_monitor.sh') do
  it { should be_executable }
end

cp -p spec/10.0.0.240/zabbix_spec.rb spec/10.0.0.241/zabbix_spec.rb

rake spec

~Omitted~
Finished in 5.09 seconds
152 examples, 0 failures[/shell]<br>I tested it without any problems.<br>It is questionable whether the test is enough.<br>```

I haven't tested the user or replication, but it seems that I can only check the standard output with the command resource type.  
The command is this ↓,  
mysql -u root -p`cat /path_to_file` -s -e "show grants for repl@'10.0.0.%';"  
I can only think of writing a test that says the return value is this ↓  
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON _._ TO 'repl'@'10.0.0.%' IDENTIFIED BY PASSWORD '\*43E209EED080057E35C2630AC06D3296\*\*\*\*\*'  
I wonder if the complex ones feel like making a check command with a simple return value.  
  
This information is also  
・HTML output can be done with rake spec SPEC\_OPTS="--format html". If it is not an error, only OK will come back, so the visibility is good when you want a report.  
・ gem install ci\_reporter and add 'ci/reporter/rake/rspec' to Rakefile.  
 Can be converted to JUnit format XML (available in Jenkins)  
・If you use [parallel\_tests](https://github.com/grosser/parallel_tests), you can run it in parallel and it's fast. It looks good when there are a lot of cars.  
・If you want to use a role or attribute, the following sites may be helpful.  
 [advanced\_tips](http://serverspec.org/advanced_tips.html)[How to Handle Host-Specific Attribute Values in Serverspec](http://mizzy.org/blog/2013/05/12/2/) [Reading chef json in Serverspec](http://blog.kenjiskywalker.org/blog/2013/07/31/serverspec-attribute/) Let's [write tests for a server environment with serverspec](http://www.slideshare.net/ikedai/serverspec)  
This is the end of the feature-length Chef series.  
Thank you for taking a long time.  
  
Click here for the back number↓  
 [Introduction of Chef-Solo and Vagrant (VPC Environment)](https://blog.colorkrew.com/chef-solo_vagrant_install/)[Environment Before Writing Recipes in Chef-Solo (Vagrantfile, role, node, data\_bags)Writing](https://blog.colorkrew.com/vagrantfile_role_data_bags/) [Recipes for Existing Procedures in Chef 1 (Default Setup)](https://blog.colorkrew.com/chef-solo_base_setting-recipe/)[Write a recipe for an existing procedure in Chef 2 (user-created)](https://blog.colorkrew.com/chef-solo_user_manage/) [Write a recipe for an existing procedure in Chef 3 (WEB server)](https://blog.colorkrew.com/chef-solo_webservers_recipe/) [Write a recipe for an existing procedure in Chef 4 (DB server)](https://blog.colorkrew.com/chef-solo_dbservers_recipe/) [Write a recipe for an existing procedure in Chef 5 (munin, zabbix)](https://blog.colorkrew.com/chef-solo_munin_zabbix_recipe/) [Test a recipe written in Chef ( serverspec)](https://blog.colorkrew.com/chef_serverspec/)  
   
   
   
   
   
   
   
   
   
 

Related Articles