ServerspecでMacの開発環境をテストする
開発用のMacBookを新調したのをきっかけに、HomebrewとAnsibleを用いて開発環境構築の自動化に取り組みました。今回はその環境をServerspecを使ってテストする方法をメモしておきます。
HomebrewとAnsibleでMacの開発環境構築を自動化する
環境
環境は以下の通りです。
ソフトウェア | バージョン |
---|---|
Mac OS X | 10.10.4 |
Homebrew | 0.9.5 |
Ansible | 1.9.1 |
Serverspec | 2.19.0 |
Serverspecとテストについて
Serverspecについては既知の方も多いと思いますが、簡単に説明すると、対象の環境が期待した通りの状態にあるかをテストするためのツールです。AnsibleやChef, Puppetといった構成管理ツールに依存することなく、また、OSの種別毎にテストコードを書き換える必要がないのが特徴です。
Serverspec
Ansibleでもテスト(のようなもの)を書こうと思えばかけますし、テストを書かなくても実行結果から状態を判断することはできます。そういった状況ではありますが、いつでもテストだけを単体で実行できるようServerspecでテストを書きました。
既にリポジトリにテストを追加しているので、READMEに書かれた手順で簡単に実施できます。以前に公開したプロビジョニングの利用者がそれなりにいるようなので、テストに関してもぜひ試していただければと思います。
mawatari/mac-provisioning – GitHub
テストを追加した時の作業ログ
自身でテストを作成した時の作業ログを残しておきます。何かの参考になれば幸いです。
Ruby環境の整備
ServerspecはRuby Gemsなので、Rubyを実行できる環境が必要です。Mac OS Xには初めからRubyはインストールされていますが、ここではSystem Rubyではなく、rbenvを利用したいと思います。環境を整えた後に、他のRubyプロジェクトと、干渉しないようにするためです。
rbenvで最新安定版のRubyをインストールするため、まずは、 web-development.ymlの rolesに ruby-buildを追加します。(ロール名は何でも構いません。)
1 2 3 4 5 6 7 8 | - hosts: localhost connection: local gather_facts: no sudo: no roles: - homebrew - homebrew-cask - ruby-build |
次に、 ruby-buildのタスクを作成します。Ansibleにはrbenvのモジュールは無いため、 shellモジュールでrbenvの最新安定版をインストールします。以下のとおりです。
1 2 3 4 5 6 7 8 9 10 11 | --- - name: Installs latest stable version of Ruby shell: rbenv install -s $(rbenv install -l | grep -v - | tail -1) register: result changed_when: '"Installed ruby" in result.stderr' - name: Switch to latest stable version of Ruby shell: rbenv global $(rbenv install -l | grep -v - | tail -1) - name: Installs latest available version of bundler. gem: name=bundler state=latest |
web-development.ymlの homebrew_packagesに rbenvが指定されていることを前提にタスクを作成しているため、 homebrew_packagesから rbenvを削除している人は、再度、追加するか、このタスクに rbenvのインストールを追加しておきましょう。
ちなみに、 rbenv install -s $(rbenv install -l | grep -v - | tail -1)というコマンドは、rbenvでRubyの最新安定版をインストールするワンライナーです。以下で解説しているので、参照してください。
rbenvでRubyの最新安定版をインストールするワンライナー
テストの作成
ひな形の作成
いよいよテストの作成です。テストを作成するにあたって、まずは bundleコマンド等を用いて、ひな形を作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | bundle init echo 'gem "serverspec"' >> Gemfile bundle install --path=vendor/bundle bundle exec serverspec-init Select OS type: 1) UN*X 2) Windows Select number: 1 Select a backend type: 1) SSH 2) Exec (local) Select number: 2 + spec/ + spec/localhost/ + spec/localhost/sample_spec.rb + spec/spec_helper.rb + Rakefile + .rspec |
これにより、19行目から24行目にある通り、4つのファイルが生成されました。 spec/localhost/sample_spec.rbに関しては、 homebrew_spec.rbという名称に変更してから利用します。
ヘルパーの作成
以下のとおり、ヘルパーを作成しました。
Homebrew Caskでインストールしたパッケージは、Serverspecの Package resource be_installed matcherが利用できないため、パッケージ名のディレクトリが存在しているかどうかを確認するテストとしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | require 'serverspec' require 'yaml' set :backend, :exec # YAMLから設定値を読み込む def load_configuration (key) configuration = YAML.load_file 'web-development.yml' configuration[0]['vars'][key].map do |package| package.kind_of?(Hash) ? package['name'] : package end end # Homebrewパッケージリストを読み込む def homebrew_packages load_configuration 'homebrew_packages' end # Homebrew Caskパッケージリストを読み込む def homebrew_cask_packages load_configuration 'homebrew_cask_packages' end # Caskroomのパスを得る def homebrew_caskroom env = Shellwords.shellsplit(ENV['HOMEBREW_CASK_OPTS'] || '').map do |option| option.split('=') end Hash[*env.flatten]['--caskroom'] || '/opt/homebrew-cask/Caskroom' end |
テストの作成
以下のとおり、テストを作成しました。
be_installedマッチャーと、 be_directoryマッチャーを使って、パッケージの存在確認を行っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 | require 'spec_helper' homebrew_packages.each do |package| describe package(package) do it { should be_installed } end end homebrew_cask_packages.each do |package| describe file(homebrew_caskroom + '/' + package) do it { should be_directory } end end |
以上でテストの作成は完了です。
テストの実行
テストを書き終えたので、実行してみたいと思います。
1 2 3 4 5 6 7 8 9 10 11 12 | bundle exec rake ... File "/opt/homebrew-cask/Caskroom/vagrant" should be directory File "/opt/homebrew-cask/Caskroom/virtualbox" should be directory Finished in 2.51 seconds (files took 0.85425 seconds to load) 30 examples, 0 failures |
すべてのテストをパスしました。面倒なことは無いと思うので、ぜひ取り組んでいただけたらと思います。以上です。
こぼれ話
テストの作成と実行に関しては以上ですが、その過程でこぼれ話もあったので、合わせてメモしておきます。
当初、Homebrewパッケージに関しても、 be_directoryマッチャーを利用してテストしようとしていました。その際、Homebrew Cellarのパスを得るヘルパーを書く必要があったのですが、そこで結構ハマりました。以下の様な内容です。
1 2 3 4 5 6 | # Homebrew Cellarのパスを得る def homebrew_cellar Bundler.with_clean_env do `brew --cellar`.chomp end end |
Bundler.with_clean_envというメソッドが肝で、これが無いと期待通りにはパスを得ることができません。Homebrewだけは、System Rubyを使ってインストールしていて、かつ、このヘルパーは bundle execで呼ばれるためです。
文字だけでは実感がわきにくい方もいると思います。CLIで以下の3つのコマンドを実行し、出力結果を見比べてもらえればと思います。
1 2 3 4 | brew --cellar ruby -e 'puts `brew --cellar`' bundle exec ruby -e 'puts `brew --cellar`' bundle exec ruby -e 'Bundler.with_clean_env { puts `brew --cellar` }' |
つまり、2行目と同じことを bundle exec下でも得たい場合は、 Bundler.with_clean_envを利用するということですね。
以上、こぼれ話でした。