Serverspecの導入とテストの書き方

# Serverspecとは
* Serverspecはサーバの状態をテストするツール
* ServerspecはRuby(Rails)で有名なテストフレームワークであるRSpecと
サーバを抽象化するライブラリであるserverinfraを合わせたツール
* テストはRSpecで書ける（Server+RSpec=Serverspec）
* Serverspecをインストールしたサーバ自身をテストするだけでなく、ネットワーク経由(ssh/WinRM)で外部の複数のサーバに対してもテストが可能 

# インストール
gemで公開されているので以下のコマンドでインストールします。

```bash
# gem install serverspec
```
# 最初にやること
作業用のディレクトリを作成して、その中で作業を行うことをおすすめします。
プログラムが無いと言われる場合は環境変数のパスを確認して下さい。

```bash
# cd /serverspec/                          #作業用ディレクトリに移動

# serverspec-init

                                           # 以下対話的な入力

Select OS type:

  1) UN*X
  2) Windows       

Select number: 1                           # UN*Xなので 1 を入力

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1                           # リモート接続のため 1 を選択

Vagrant instance y/n: n                    # vagrantかどうか聞かれるので今回は n を選択

Input target host name: test-server    #テストするサーバのホスト名を入力

```
ここまで入力を行うと初期設定用に下記のファイルが作成されます。

```bash:serverspec/
├─ spec
│  ├─ test-server
│  │  └─ sample_spec.rb
│  └─ spec_helper.rb
├─ Rakefile
└─ .rspec
```
### ファイル説明
* spec/test-server/sample_spec.rb
  * 実際にテストする内容を記載するファイル
  * 同ディレクトリに *_spec.rb というファイル名で別ファイルを作成すればそちらもテストされます
* spec/spec_helper.rb
  * テストに関する全般的な設定ファイルです
* Rakefile
  * テスト実行のためのタスク定義ファイルです
* .rspec
  * RSpecの挙動を設定するファイルです 

# 実行
デフォルトのサンプルで入っている設定で、リモートのCentOS 7サーバでapacheが動いているかを確認します。

```bash
# rake spec:test-sever # ドメインは各自のものに読み替えてください
```
もしも

```bash
Please set sudo password to Specinfra.configuration.sudo_password.
```
と出て実行できない場合は下記のコマンドを実行してください。

```bash
# rake spec:www.example.com ASK_SUDO_PASSWORD=1
```
ASK_SUDO_PASSWORD=1 は sudoのパスワードを対話的に入力しその後実行します。

うまく行けば以下のようになると思います。

```ruby
# rake spec:jazz.workapart.org ASK_SUDO_PASSWORD=1
Enter sudo password:

Package "httpd"                  #パッケージがインストールされているか確認
  should be installed

Service "httpd"                  #サービスが起動していて自動起動設定されているか確認
  should be enabled
  should be running

Port "80"                        #指定のポートをListenしているか確認
  should be listening

Finished in 1.48 seconds (files took 6.75 seconds to load)
4 examples, 0 failures
```
# テストの基本
## インストール系

```ruby:serverspec/spec/www.example.com/*_spec.rb
# パッケージがインストールされているか確認する
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
```
## 汎用コマンドによる確認

```ruby:serverspec/spec/www.example.com/*_spec.rb
# コマンドの標準出力から指定のバージョンがインストールされているか確認する
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
```
## サービスの起動確認

```ruby:serverspec/spec/www.example.com/*_spec.rb
# 指定のサービスが起動していて自動起動設定されているか確認する
describe service('elasticsearch') do
  it { should be_enabled }
  it { should be_running }
end

# 指定のポートをListenしているか確認する
describe port("9200") do
  it { should be_listening }
end
```
## ユーザとグループ

```ruby:serverspec/spec/www.example.com/*_spec.rb
# グループが存在するか確認する
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
```
## ファイルを確認する

```ruby:serverspec/spec/www.example.com/*_spec.rb
# ファイルの中身が指定の文字列にマッチするか確認する
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
```
## その他

```ruby:serverspec/spec/www.example.com/*_spec.rb
# 名前解決できるか確認する
zabbix_host = 'zabbix'
describe host(zabbix_host) do
  it { should be_resolvable.by('hosts') }
end
```
# 参考
[Serverspecでサーバの構成をテストする 導入と個人的知見](https://qiita.com/acairojuni/items/b1a71f4720bbcb175e96)
[Serverspecでよく使うテストの書き方まとめ](https://qiita.com/minamijoyo/items/467ddd13c0cab15330bf)
[Serverspecことはじめ](https://qiita.com/hikao/items/d925923648e6d1e09c65)
[]()