Serverspec のインストールと基本的な使い方

# はじめに
Infrastructure as Codeを推進するべく、今更感もありますが、テストにServerspecを使用してみたいと思いますよ！

# 環境
- Serverspecを実行する方
  - ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]
  - CentOS Linux release 7.2.1511 (Core)
- テストを受ける方
  - ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-linux]
  - CentOS release 6.8 (Final)

# 本記事で触れること
- Serverspecのインストール
- sample_spec.rbを使ったテスト

※SSHでのテストとなります

# インストール＆セットアップ
## Rubyのインストール
ServerspecはRspecを利用したテスティングフレームワークなので、

もちろんRubyのインストールが必須となります

以下のようなスクリプトでも書いてささっとインストールしてしまいましょう

```bash
#!/bin/bash

# rbenvと関連プラグインのダウンロード
cd /opt
git clone https://github.com/sstephenson/rbenv.git
mkdir -p /opt/rbenv/plugins
cd /opt/rbenv/plugins
git clone https://github.com/sstephenson/ruby-build.git

# .bashrcへのrbenv関連設定追加
grep "rbenv global config" ${HOME}/.bashrc > /dev/null 2>&1
if [ $? -ne 0 ]; then
  echo >> ~/.bashrc
  echo "# rbenv global config" >> ~/.bashrc
  echo 'export RBENV_ROOT="/opt/rbenv"' >> ~/.bashrc
  echo 'export PATH="${RBENV_ROOT}/bin:${PATH}"' >> ~/.bashrc
  echo 'eval "$(rbenv init -)"' >> ~/.bashrc
fi

# Ruby関連モジュールのインストール
yum -y install gcc make openssl-devel libffi-devel ruby-devel readline-devel rubygems sqlite-devel bzip2

# Rubyのインストール
/bin/bash -lc "rbenv install 2.3.0"
/bin/bash -lc "rbenv rehash"
/bin/bash -lc "rbenv global 2.3.0"
```

## Serverspecのインストール
### Serverspecのインストール
```zsh
% gem install serverspec
```

### Serverspec初期セットアップ
```zsh
% serverspec-init
```

とすると、こんな画面が出てきます。
※{HOSTNAME}の部分は読み替えて下さい

```
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: {HOSTNAME}
     + spec/
     + spec/{HOSTNAME}/
     + spec/{HOSTNAME}/sample_spec.rb
     + spec/spec_helper.rb
```

### SSH設定
SSHで接続してテストを実行するため、下記コマンドでSSH鍵をリモートホストへ渡します

```zsh
% ssh-copy-id ${HOSTNAME}
/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@${HOSTNAME}'s password: ※パスワードを入力
```

# テストの実行
すでにカレントディレクトリには以下のような構成でファイルが生成されているので、このままテストをしてみます

```text:ディレクトリ構成
spec/
spec/{HOSTNAME}/
spec/{HOSTNAME}/sample_spec.rb
spec/spec_helper.rb
```

以下のコマンドでテストを実行

```zsh
% rake spec
(in /root/serverspec/work/20170122)
/opt/rbenv/versions/2.2.3/bin/ruby -I/opt/rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.5.0/lib:/opt/rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.5.4/lib /opt/rbenv/versions/2.2.3/lib/ruby/gems/2.2
.0/gems/rspec-core-3.5.4/exe/rspec --pattern spec/${HOSTNAME}/\*_spec.rb

Package "httpd"
  should be installed (FAILED - 1)

Service "httpd"
  should be enabled (FAILED - 2)
  should be running

Port "80"
  should be listening

Failures:

  1) Package "httpd" should be installed
     On host `${HOSTNAME}'
     Failure/Error: it { should be_installed }
       expected Package "httpd" to be installed
       /bin/sh -c rpm\ -q\ httpd
       package httpd is not installed

     # ./spec/${HOSTNAME}/sample_spec.rb:4:in `block (2 levels) in <top (required)>'

  2) Service "httpd" should be enabled
     On host `${HOSTNAME}'
     Failure/Error: it { should be_enabled }
       expected Service "httpd" to be enabled
       /bin/sh -c chkconfig\ --list\ httpd\ \|\ grep\ 3:on

     # ./spec/${HOSTNAME}/sample_spec.rb:12:in `block (2 levels) in <top (required)>'

Finished in 0.83381 seconds (files took 1.49 seconds to load)
4 examples, 2 failures

Failed examples:

rspec ./spec/${HOSTNAME}/sample_spec.rb:4 # Package "httpd" should be installed
rspec ./spec/${HOSTNAME}/sample_spec.rb:12 # Service "httpd" should be enabled

/opt/rbenv/versions/2.2.3/bin/ruby -I/opt/rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-support-3.5.0/lib:/opt/rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/rspec-core-3.5.4/lib /opt/rbenv/versions/2.2.3/lib/ruby/gems/2.2
.0/gems/rspec-core-3.5.4/exe/rspec --pattern spec/${HOSTNAME}/\*_spec.rb failed
```

## テスト結果の検証
まずは実行されたsample_spec.rbを確認

```ruby
require 'spec_helper'

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

describe service('httpd'), :if => os[:family] == 'redhat' do
  it { should be_enabled }
  it { should be_running }
end

describe service('apache2'), :if => os[:family] == 'ubuntu' do
  it { should be_enabled }
  it { should be_running }
end

describe service('org.apache.httpd'), :if => os[:family] == 'darwin' do
  it { should be_enabled }
  it { should be_running }
end

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

これは以下のテストとなっていて、
且つOS毎に判定して条件分岐しているテスト内容となっています

- Apacheがインストールされているか
- Apacheが起動しているか
- Apacheが自動起動設定されているか
- Port80がリッスンしているか

テスト内容的にはとあるパッケージソフトに入っているApacheなので、通常のApacheはインストールされていない、且つ、起動はしているが自動起動設定はされておらず、ポートはリッスンしているという正しいテスト結果をなっています

## おわりに
はじめてのServerspecでしたが、色々なテストができそうなので自分なりのベスト・プラクティスを構築してInfrastructure as Codeを実施していければと思います！