Edited at

Serverspecでよく使うテストの書き方まとめ

More than 1 year has passed since last update.


はじめに

インフラ周りのテストにServerspecを使ってますが、よく書くテストの書き方がある程度パターン化してきたので、コピペで使えるサンプルの例文集としてまとめておきます。

この記事の目的は特に機能の網羅性をカバーしようというわけではありません。よくあるユースケースとして、Serverspecでこーゆーのどう書くの?と思ったときのスニペットとしてご利用下さい。


基本


インストール関連


パッケージがインストールされているか確認する

describe package('git') do

it { should be_installed }
end


複数のパッケージがインストールされているかまとめて確認する

%w{autoconf bison flex gcc gcc-c++ kernel-devel make m4}.each do |pkg|

describe package(pkg) do
it { should be_installed }
end
end


指定のバージョンのパッケージがインストールされているか確認する

describe package('td-agent') do

it { should be_installed.with_version('1') }
end

上記はメジャーバージョンだけ指定した場合だけど、細かいバージョンも指定できる。


gemとして指定のバージョンがインストールされているか確認する

describe package('bundler') do

it { should be_installed.by('gem').with_version('1.10.5') }
end


fluentプラグインなど組込gemとしてインストールされているか確認する

plugins = %w(config-expander datacounter numeric-counter zabbix forest)

plugins.each do |plugin|
describe package("fluent-plugin-#{plugin}") do
let(:path) { '/usr/lib64/fluent/ruby/bin:$PATH' }
it { should be_installed.by(:gem) }
end
end


汎用コマンドによる確認


コマンドの標準出力から指定のバージョンがインストールされているか確認する

describe command('ruby -v') do

its(:stdout) { should match /ruby 2\.1\.4/ }
end


コマンドのリターンコードからパスが通っているか確認する

describe command('which mysql') do

its(:exit_status) { should eq 0 }
end


sudoせずコマンドを実行する

describe command('which mysql') do

let(:disable_sudo) { true }
its(:exit_status) { should eq 0 }
end


sudoするユーザを指定する

describe command('which mysql') do

let(:sudo_options) { '-u user01 -i'}
its(:exit_status) { should eq 0 }
end


サービスの起動確認


指定のサービスが起動していて自動起動設定されているか確認する

describe service('elasticsearch') do

it { should be_enabled }
it { should be_running }
end


指定のポートをListenしているか確認する

describe port("9200") do

it { should be_listening }
end


curlでHTTPアクセスして200 OKが返ってくるか確認する

describe command('curl http://127.0.0.1:9200/_plugin/head/ -o /dev/null -w "%{http_code}\n" -s') do

its(:stdout) { should match /^200$/ }
end


ユーザとグループ


グループが存在するか確認する

describe group('ec2-user') do

it { should exist }
end


ユーザが指定のグループに所属しているか確認する

describe user('ec2-user') do

it { should belong_to_group 'ec2-user' }
end


ユーザが指定のUIDを持っているか確認する

describe user('td-agent') do

it { should have_uid 403 }
end


ファイルを確認する


ファイルの中身が指定の文字列にマッチするか確認する

describe file('/etc/sysconfig/clock') do

its(:content) { should match /ZONE="Asia\/Tokyo"/ }
end


ファイルに読み込み権限があるか確認する

%w{

/var/log/httpd/access.log
/var/log/httpd/error.log
}
.each do |logfile|
describe file(logfile) do
it { should be_readable.by_user('td-agent') }
end
end


ディレクトリのオーナーとパーミッションを確認する

home_dir = "/home/ec2-user"

describe file("#{home_dir}/.ssh") do
it { should be_directory }
it { should be_owned_by('ec2-user') }
it { should be_grouped_into('ec2-user') }
it { should be_mode '700' }
end


その他


名前解決できるか確認する

zabbix_host = 'zabbix'

describe host(zabbix_host) do
it { should be_resolvable.by('hosts') }
end


応用的なトピック


動的な情報の取得


動的にテスト対象のホスト名を取得する

hostname = host_inventory['hostname']

describe file('/etc/sysconfig/network') do
its(:content) { should match /HOSTNAME=#{hostname}/ }
end


RedHat系かどうか判定する

if os[:family] == 'redhat'

...
else
...
end

elseがない場合は、以下のような書き方もできる

describe yumrepo('epel'), :if => os[:family] == 'redhat' do

it { should exist }
it { should be_enabled }
end


Amazon Linuxかどうか判定する


spec_helper.rb

def os_platform_amazon?

Specinfra.backend.run_command('uname -r').stdout.include?("amzn1")
end

require 'spec_helper'

if os_platform_amazon?
%w{aws-cli s3cmd}.each do |pkg|
describe package(pkg) do
it { should be_installed }
end
end
end

(2015/10/21追記) 現在の最新のserverspecではif os[:family] == 'amazon' で判定できるようです。これはserverspecの基盤となっているspecinfra(v2.36.0)でos[:family]にamazonが判定できるようなったからで、バージョンの依存関係から対応するserverspec(v2.4.0)以降から使えるはず。


テストケースの共有


複数のサーバ種別で共通するテストケースを使いたい


shared/commons_examples.rb

require 'spec_helper'

shared_examples 'commons' do
describe "commons spec" do
describe package('git') do
it { should be_installed }
end
end
end



spec_helper.rb

base_spec_dir = Pathname.new(File.join(File.dirname(__FILE__)))

Dir[base_spec_dir.join('shared/**/*.rb')].sort.each{ |f| require f }



webapp/app_server_spec.rb

require 'spec_helper'

describe "app server spec" do
include_examples 'commons'
end



webapp/web_server_spec.rb

require 'spec_helper'

describe "web server spec" do
include_examples 'commons'
end



テスト対象の制御


テストケースをロール単位で管理したい

以前ブログに書いた以下の記事を参照↓

Serverspecのテストケースをロール単位で管理する


テスト対象のIPとロールを指定する

以前ブログに書いた以下の記事を参照↓

Serverspecでテスト対象のIPとロールを指定する


カスタムマッチャを作りたい


YAMLとしてパース出来るかどうかテストしたい

以前Qiitaに書いた以下の記事を参照↓

serverspecでカスタムマッチャを定義してYAML形式としてパース出来るかどうかをテストするbe_yamlを作ってみる


おわりに

書き出してみたら色々あったけど、何か忘れてるものあったらor新しい小技を覚えたら、随時追記します。