のんびりSEの議事録

プログラミング系のポストからアプリに関してのポストなどをしていきます。まれにアニメ・マンガなど

【Serverspec】構築したサーバはServerspecを使ってテストしよう

AnsibleやChefといった、プロビジョニングツールを用いてサーバ環境を構築したので、次はServerspecを使ってちゃんとインストールされているか等、テストをしないとなということで、Serverspecの紹介です。

Serverspecとは

サーバ向けのテストフレームワークで、Rubyで実装されており、RubyのテストフレームワークRSpec」の書き方でテストが書ける。
しかし、Serverspecの真の目的は、インフラストラクチャコードのリファクタリング支援すること。

インストール

*Rubyが既にインストールされていることが前提

$ gem install serverspec

or

  • Gemfile
gem 'serverspec'

 

$ bundle install

初期設定

$ serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: www.example.jp
 + spec/
 + spec/default/
 + spec/default/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec
  1. OSの種類を聞かれるので、テスト対象のOSの番号を入力する
  2. バックエンドの種類を聞かれるので、テスト対象はSSH接続する必要があるなら1、ローカルに対して行う場合は2を入力する
  3. Vagrantインスタンスに対して行うかどうかを聞かれる

Rakefile

生成されたRakefileは以下のようになっている

require 'rake'
require 'rspec/core/rake_task'

task :spec    => 'spec:all'
task :default => :spec

namespace :spec do
  targets = []
  Dir.glob('./spec/*').each do |dir|
    next unless File.directory?(dir)
    target = File.basename(dir)
    target = "_#{target}" if target == "default"
    targets << target
  end

  task :all     => targets
  task :default => :all

  targets.each do |target|
    original_target = target == "_default" ? target[1..-1] : target
    desc "Run serverspec tests to #{original_target}"
    RSpec::Core::RakeTask.new(target.to_sym) do |t|
      ENV['TARGET_HOST'] = original_target
      t.pattern = "spec/#{original_target}/*_spec.rb"
    end
  end
end

spec以下のディレクトリをホスト名として使用している
環境変数にtarget hostnameをセットしているので、key名 'TARGET_HOST'は空けておいた方が無難か...

spec_helper.rb

localhostの場合

require 'serverspec'

set :backend, :exec

たったこれだけ

SSHの場合

require 'serverspec'
require 'net/ssh'

set :backend, :ssh

if ENV['ASK_SUDO_PASSWORD']
  begin
    require 'highline/import'
  rescue LoadError
    fail "highline is not available. Try installing it."
  end
  set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
else
  set :sudo_password, ENV['SUDO_PASSWORD']
end

host = ENV['TARGET_HOST']

options = Net::SSH::Config.for(host)

options[:user] ||= Etc.getlogin

set :host,        options[:host_name] || host
set :ssh_options, options

# Disable sudo
# set :disable_sudo, true


# Set environment variables
# set :env, :LANG => 'C', :LC_MESSAGES => 'C' 

# Set PATH
# set :path, '/sbin:/usr/local/sbin:$PATH'
注意点

Root以外のユーザで実行する場合、環境変数 'SUDO_PASSWORD'にパスワードをセットする必要がある

$ SUDO_PASSWORD=xxxxxxxx rake spec

パスワード入力プロンプトを立ち上げて入力する場合

$ ASK_SUDO_PASSWORD=1 rake spec

※ 別途highlineをインストールする必要がある

Vagrant

require 'serverspec'
require 'net/ssh'
require 'tempfile'

set :backend, :ssh

if ENV['ASK_SUDO_PASSWORD']
  begin
    require 'highline/import'
  rescue LoadError
    fail "highline is not available. Try installing it."
  end
  set :sudo_password, ask("Enter sudo password: ") { |q| q.echo = false }
else
  set :sudo_password, ENV['SUDO_PASSWORD']
end

host = ENV['TARGET_HOST']

`vagrant up #{host}`

config = Tempfile.new('', Dir.tmpdir)
config.write(`vagrant ssh-config #{host}`)
config.close

options = Net::SSH::Config.for(host, [config.path])

options[:user] ||= Etc.getlogin

set :host,        options[:host_name] || host
set :ssh_options, options

# Disable sudo
# set :disable_sudo, true


# Set environment variables
# set :env, :LANG => 'C', :LC_MESSAGES => 'C' 

# Set PATH
# set :path, '/sbin:/usr/local/sbin:$PATH'

パスワード周りは普通のSSHと同じ。
タスク内で vagrant upを実行している。
vagrant ssh-configを内部で実行し、ssh情報をTempfileに保管している。

テスト実行

rakeコマンドで実行。

$ rake spec

 

$ rake -vT
rake spec:_default   # Run serverspec tests to default
rake spec:localhost  # Run serverspec tests to localhost

Support OS

マルチサポートで一応テストもしているっぽい

明示的にOSを指定する場合は以下のように記述する

require 'serverspec'
set :os, :family => 'redhat', :release => '7', :arch => 'x86_64'

Resource Types

package

パッケージがインストールされているかどうかのテスト

describe package('httpd') do
  it { should be_installed }
end

service

サービスに関するテスト

describe service('htpd') do
  it { should be_enabled } #自動起動が有効になっているか
  it { should be_running } #起動しているか
end

port

ポートに関するテスト

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

file

ファイルに関するテスト

#ファイルが存在するか
describe file('/etc/passwd') do
  it { should be_file } 
end

# ディレクトリが存在するか
describe file('/var/log/httpd') do
  it { should be_directory }
end

command

コマンド実行した出力結果に対してのテスト

describe command('ls -al /') do
  its(:stdout) { should match /bin/ }
end

describe command('ls /foo') do
  its(:stderr) { should match /No such file or directory/ }
end

describe command('ls /foo') do
  its(:exit_status) { should eq 0 }
end

参考

本家サイト

Serverspec - Home