test-kitchen + serverspecで行うchef-repoのテスト

# test-kitchen + serverspecで行うchef-repoのテスト
test-kitchenの使い方を調べたり検証していたりしたのですが、インスタンスを作成してプロビジョニングするのに結構時間がかかって暇なので、書いてみました。

## test-kitchenについて
test-kitchenとは、任意に作成したインスタンス上に環境を構築し、その結果の検証が可能なツールです。インスタンスを作成するためのドライバはデフォルトでvagrantが選択されますが、vagrant以外、例えばkitchen-dockerを使えばdocker上でテストを行うことも可能です。

test-kitchen
[https://github.com/test-kitchen/test-kitchen.git](https://github.com/test-kitchen/test-kitchen.git)

## 初期設定 〜 インスタンスの構築

実際にtest-kitchenとserverspecを使ったchef-repoのテストをやってみます。

### test-kitchenの挙動
test-kitchenでは、以下の流れでインスタンス作成からテストまでを行います。

1. インスタンスの作成
+ chef-soloの実行で設定に応じた環境へ収束
+ インスタンスの検証

### 事前準備

それぞれ入っていないツールがあれば入れておきましょう。

```bash
$ gem install chef
$ gem install berkshelf
$ gem install test-kitchen
$ gem install serverspec
```

### 初期設定
``kitchen init`` を実行すると、.kitchenディレクトリおよび.kitchen.ymlが生成されます。

#### .kitchen.yml

このファイルがtest-kitchen実行時の設定ファイルです。パラメータについて簡単に確認しておきましょう。

- driver：テストを実行する仮想環境のドライバです。
- provisioner：プロビジョニングツールです。
- platforms：インスタンスを作成し、テストを実行する環境です。デフォルトではubuntuとcentosの２つが検証用プラットフォームとして用意されています。
- suites：テストスイートです。run_listに指定したレシピやロールが実行されます。実行時のパラメータはattributesで指定することが可能です。

今回は、試しにサードパーティのapache2を適用してみます。また、私の場合はも専らcentosしか使わないので、ubuntuの指定は削除しました。

```yaml:.kitchen.yml
driver:
  name: vagrant

provisioner:
  name: chef_solo

platforms:
#  - name: ubuntu-12.04
  - name: centos-64

suites:
  - name: app
    run_list:
      - recipe[apache2::default]
    attributes:
      platform_family: "rhel"
```

### Berksfileの設定
慣例上、cookbooksにはサードパーティのcookbookを入れ、サイト独自のレシピを使用する場合は、site-cookbooksにレシピを入れることが多いと思います。

一方、berkshelfはデフォルトでcookbooksをberkshelfの管理対象のディレクトリとします。 
test-kitchenでの検証を行う場合、site-cookbooks内にあるcookbookもberkshelfの管理下に入れる必要があります。

```yaml:Berksfile
source "http://api.berksfile.com" # berkshelfのバージョンが<3の場合は site: opscode

cookbook "apache2"
cookbook "hoge", path: "site-cookbooks/hoge" # ローカルのsite-cookbooksに直接存在するcookbook
cookbook "fuga", git: "git:fuga@fuga-repository.com" # gitのリポジトリ上に存在するcookbook
```
### インスタンスの収束

convergenceとは英語で収束の意です。Chefは何度同じ処理を実行しても同じ環境に収束するという冪等性を備えていることからこのような名前がついているのだと思います。

以下のコマンドを実行することで、chefのレシピをインスタンスに適用させることができます。

```
$ kitchen converge [INSTANCE]
```

成功すれば、以下のように表示されるはずです。

```
       [2014-05-03T16:13:59+00:00] INFO: Report handlers complete
       Chef Client finished, 67/74 resources updated in 67.515359435 seconds
       Finished converging <default-centos-64> (2m18.63s).
```

## test-kitchenとserverspecによる検証


さて、ここまででインスタンスを.kitchen.ymlに記述した状態へ（たぶん）収束させることができました。
ただ、この状況で想定した通りの状態に収束しているのかどうか？を確認するテストをしたいと思いますよね。

これを検証するためのテストとして、今回は serverspec を使用します。

```bash
# テスト用ディレクトリ
$ mkdir -p CHEF-REPO_PATH/test/integration/SUITE_NAME/TESTING_FRAMEWORK/

# テスト用ファイルの作成
$ touch CHEF-REPO_PATH/test/integration/SUITE_NAME/TESTING_FRAMEWORK/*_spec.rb
```

test-kitchenのチュートリアルにも記載されていますが、各ディレクトリは以下の意味があります。

- test/integration： テストが含まれるディレクトリで、デフォルトはtest/intergrationです。
- SUITE_NAME： .kitchen.ymlに記述したスイートの名前です。
- TESTING_FRAMEWORK： テスティングフレームワークの名前です。今回はserverspecを利用します。

### Serverspecでテストを記述

とりあえず、以下のようにvagrantユーザについてのテストを書いてみました。

```ruby:test/integration/default/serverspec/vagrant_spec.rb
require 'serverspec'

set :backend, :exec

# vagrantユーザについて
describe user("vagrant") do
  it { should exist } # vagrantユーザが存在する
  it { should belong_to_group "wheel" } # vagrantユーザはwheelに所属している
end
```

今回はapacheをインストールしたので、実際にapacheが動いているかどうか？も検証してみます。

```ruby:test/integration/default/serverspec/apache2_spec.rb
require 'serverspec'

set :backend, :exec

# httpdがインストールされている
describe package("httpd") do
  it { should be_installed }
end

# httpdプロセスが稼働している
describe process("httpd") do
  it { should be_running }
end

# TCP80番で待ち受けている
describe port("80") do
  it { should be_listening.with("tcp") }
end
```

### インスタンスの検証
以下のコマンドを実行すると、テストを実行できます。

```bash
$ kitchen verify [INSTANCE]
```
問題ない場合は、以下のように結果が表示されるはずです。

```bash
-----> Starting Kitchen (v1.2.1)
-----> Verifying <default-centos-64>...
       Suite path directory /tmp/busser/suites does not exist, skipping.
       Uploading /tmp/busser/suites/serverspec/apache2_spec.rb (mode=0644)
       Uploading /tmp/busser/suites/serverspec/vagrant_spec.rb (mode=0644)
-----> Running serverspec test suite
       /opt/chef/embedded/bin/ruby -I/tmp/busser/suites/serverspec -S /opt/chef/embedded/bin/rspec /tmp/busser/suites/serverspec/apache2_spec.rb /tmp/busser/suites/serverspec/vagrant_spec.rb --color --format documentation
       
       Package "httpd"
         should be installed
       
       Process "httpd"
         should be running
       
       Port "80"
         should be listening
       
       User "vagrant"
         should exist []
         should belong to group "wheel"
       
       Finished in 0.35567 seconds
       5 examples, 0 failures
       Finished verifying <default-centos-64> (0m2.52s).
-----> Kitchen is finished. (0m3.54s)
```
### まとめ
こんな感じで、chef-repoで作成した環境の構成からテストまでを一通りまとめてすることができます。
test-kitchenによって、chef-repoの内容をだいぶ検証しやすくなったのではないでしょうか？

一方で、個人的には以下の点が何かしら改善されるといいなと思います。

+ nodes/rolesの内容をtest-kitchenで参照できず、すべて.kitchen.yml に記述しなければならない
+ インスタンスを収束させるのに時間がかかる

とはいえ便利なものなので、積極的に使っていきたいですね。

## 参考リンク

参考にしたページを簡単にまとめておきます

- [test-kitchen/test-kitchen](https://github.com/test-kitchen/test-kitchen) Github
- [test-kitchen](http://kitchen.ci/) test-kitchen公式
- [serverspec](http://serverspec.org/) serverspecの公式
- [test-kitchenのつかいかた](http://qiita.com/sawanoboly/items/9f560bd63ad0712b17ba) コマンドのまとめが分かりやすい
- [既存のchef cookbookをtest-kitchen対応させていく。](http://qiita.com/futoase/items/3a77a7d86ea896488b6b) serverspecに対応させる例