ブログ

ryuzeeによるブログ記事。不定期更新

test-kitchenを使ってChefのレシピを複数環境でテストする方法

test-kitchenはopscodeが提供するChefのレシピをテストするための仕掛けで、Vagrantを使って複数のOSやOSのバージョンを立ち上げレシピをテストすることができる。(Vagrant以外も使える) テストはminitestやcucumberなどを使って記述する。 テストの流れは以下のようになる。

  • 設定ファイルに記載されたOSをVagrantで起動する(既にOSが起動している場合はそのまま利用する。ひな形となるbaseboxが存在しない場合は、設定ファイルに記載された入手元からbaseboxをダウンロードする)
  • Vagrant側とレシピが共用され、レシピが実行される
  • レシピ実行後、テストが実行される
  • テストが終了すると、OSの設定が複数あれば次のOSを使ってテストする

では早速設定を行なってみよう。まず1つのレシピをテストする場合だ。

1つのレシピをテストする場合

まずトップディレクトリに移動する。すなわち以下のような構成の場所だ。(なお今回のテストには僕が使っているanalogインストールのレシピを使った)

├── CHANGELOG.md
├── README.md
├── attributes
├── definitions
├── libraries
├── metadata.rb
├── providers
├── recipes
├── resources
└── templates

まずGemfileを作成する。内容は以下の通り。

source :rubygems
gem 'test-kitchen'
gem 'minitest'
gem 'minitest-chef-handler'

ファイルの作成が終わったら以下のコマンドを実行してモジュールを導入する。

bundle install

ついでテスト用のディレクトリを初期化する。

bundle kitchen init

これによってtestディレクトリが作られる。テストディレクトリには、kitchen/Kitchenfile というファイルが作られている。 デフォルトでは、レシピ名が書かれたブロックのみが存在している。 なお、この状態だと、テストはopscodeのCentOS5.8, CentOS6.3、Ubuntu10.04、Ubuntu12.04で行われるようになっている。

以下のKitchenfileの例では、ubuntuでのテストはせず、自前のVagrantのbaseboxを使って、CentOS 5.8 / 6.2 / 6.3をテストする設定にしている。

# vim: ft=ruby

platform :centos do
  version "6.3" do
    box "centos_63_x86_64_ja"
    box_url "https://dl.dropbox.com/u/428597/vagrant_boxes/centos_63_x86_64_ja.box"
  end

  version "6.2" do
    box "centos_62_x86_64_ja"
    box_url "https://dl.dropbox.com/u/428597/vagrant_boxes/centos_62_x86_64_ja.box"
  end

  version "5.8" do
    box "centos_58_x86_64_ja"
    box_url "https://dl.dropbox.com/u/428597/vagrant_boxes/centos_58_x86_64_ja.box"
  end
end

cookbook "analog" do
  exclude :platform => 'ubuntu'
end

いまはレシピにテストが用意されていないので、テストを作成しよう。 minitestを使った場合は、レシピのトップディレクトリにfiles/default/tests/minitest ディレクトリを作成する。 その上で、以下のファイルを作る

files/default/tests/minitest/default_test.rb

内容は以下のようなものになる。この例では、analogをインストールするレシピをテストしており、テストの中では、analogがパッケージでインストールされ、実行ファイルは/usr/bin/analogに配置され、パーミッションは0755であることをテストしている(もちろんサンプルなのでもっと色々テストは書ける)。

# vim: ft=ruby

describe 'analog' do
  require 'chef/mixin/shell_out'
  include Chef::Mixin::ShellOut
  include MiniTest::Chef::Assertions
  include MiniTest::Chef::Context
  include MiniTest::Chef::Resources

  it 'installs analog' do
    # minitest-chef-handler
    package('analog').must_be_installed
    file('/usr/bin/analog').must_exist

    # minitest
    assert File.exist?('/usr/bin/analog')
  end

  it 'has correct permission' do
    file('/usr/bin/analog').must_have(:mode, "0755")
  end
end

ここまでできたらテストを実行してみよう。テストはレシピのトップディレクトリで実行する。

bundle exec kitchen test

これを実行すればテストが走る。テストの際にはfoodcriticによるレシピの内容チェックも行われる。 冒頭で以下のように表示されるはずだ(もちろん作ったレシピによって警告の内容は異なる)。

FC007: Ensure recipe dependencies are reflected in cookbook metadata: /path/to/cookbooks/analog/recipes/default.rb:11
FC007: Ensure recipe dependencies are reflected in cookbook metadata: /path/to/cookbooks/analog/recipesdefault.rb:12
FC007: Ensure recipe dependencies are reflected in cookbook metadata: /path/to/cookbooks/analog/recipesdefault.rb:13
FC007: Ensure recipe dependencies are reflected in cookbook metadata: /path/to/cookbooks/analog/recipesdefault.rb:14

なお、指定したOS/バージョンのみテストを実行したい場合は

bundle exec kitchen test --platform cents-5.8

のようにオプションを指定する。また、テスト実行後にVagrantのインスタンスを破棄したい場合は

bundle exec kitchen test --teardown

のようにすると良い。もちろん後からインスタンスを破棄することもできる。その場合は

bundle exec kitchen destroy --platform centos-5.8

のようにする。なお、プラットフォームの指定の仕方はKitchenfileで定義しているOS名-バージョン名となる。 現在のインスタンスの起動状況を確認したい場合は

bundle exec kitchen status

とすると、インスタンスの動作状況が以下のように表示される。

Current VM states:
centos-5.8 running
centos-6.2 running
centos-6.3 running
ubuntu-10.04 not created
ubuntu-12.04 not created

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.

また作ったVMにログインする場合は以下のようにする。

bundle exec kitchen ssh --platform centos-6.2

利用可能なOSとバージョンのリストを表示する場合は

bundle exec kitchen platform list

とすれば

The source :rubygems is deprecated because HTTP requests are insecure.
Please change your source to 'https://rubygems.org' if possible, or 'http://rubygems.org' if not.
  centos-5.8
  centos-6.2
  centos-6.3
  ubuntu-10.04
  ubuntu-12.04

のように表示される。

これで1つのレシピのテストが複数のプラットフォームでおこなうことが可能になった。 次に複数のレシピを同時にテストする方法について見ていこう。

複数のレシピをテストする場合

複数のレシピをまるごとテストするオプションは用意されていないので一捻り必要だ。すなわち全部をテストするためのレシピを作成する必要がある。僕は、別のディレクトリにテスト専用のレシピを作って実現したのでその方法を紹介する。

テスト専用レシピの作成

レシピ格納用のフォルダを作り、その中で
knife cookbook create all_tests -o .

としてレシピを作る。また、このディレクトリの中で、冒頭のケースと同じようにGemfileを作成し、その後、

bundle exec kitchen init

としてtestディレクトリを作成する。ここでもKitchenfileが作られるので、今度は内容を以下のようにする。

# vim: ft=ruby

platform :centos do
  version "6.3" do
    box "centos_63_x86_64_ja"
    box_url "https://dl.dropbox.com/u/428597/vagrant_boxes/centos_63_x86_64_ja.box"
  end

  version "6.2" do
    box "centos_62_x86_64_ja"
    box_url "https://dl.dropbox.com/u/428597/vagrant_boxes/centos_62_x86_64_ja.box"
  end

  version "5.8" do
    box "centos_58_x86_64_ja"
    box_url "https://dl.dropbox.com/u/428597/vagrant_boxes/centos_58_x86_64_ja.box"
  end
end

cookbook "all-tests" do
  exclude :platform => 'ubuntu'
  # テストしたい名前を列挙する
  run_list_extras ["apache_mysql_php", "trac", "analog"]
end

レシピの作成

次にテスト対象となるレシピを修正する。レシピはrecipes/default.rb だ。内容を以下のようにする。 単純にテストしたいレシピをinclude_recipeで列挙すればOKだ。
include_recipe "analog"
include_recipe "apache_mysql_php"
include_recipe "trac"

依存するレシピの配置

そして最後に上記のレシピをtest/kitchen/cookbooksに配置する。 すなわちこのようになる。
test/kitchen/cookbooks
├── README.md
├── analog
├── apache_mysql_php
├── (略)
└── trac

ここまでできたらテストが実行可能だ。トップディレクトリで

bundle exec kitchen test

とすれば良い。これで複数のテストもまとめて実行できる。

画面の出力はこんな感じだ。

なお、いままでの説明だと分かりにくいかもしれないが、上記を設定した内容を僕のgithubにおいておいた。 https://github.com/ryuzee/cookbooks-test https://github.com/ryuzee/cookbooks 僕の場合はcookbook-testの方で、cookbooksをsubmoduleとして登録しておくようにしている。

アジャイルコーチングやトレーニングを提供しています

株式会社アトラクタでは、アジャイル開発に取り組むチーム向けのコーチングや、認定スクラムマスター研修などの各種トレーニングを提供しています。ぜひお気軽にご相談ください。

詳細はこちら