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

  • 151
    Like
  • 0
    Comment

はじめに

インフラ周りのテストに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新しい小技を覚えたら、随時追記します。