serverspecのテストケースを育ててみた

前回の記事で作成したserverspecのテストケースを育ててみました。Ruby on Railsチュートリアルの写経を始めました - m-namikiの日記のときに作成したレシピを対象にします。レシピはこんなことをやっていました。

  • iptablesの停止、無効化
  • epel、remiをyumリポジトリに追加
  • sqlite-develのインストール
  • rbenvでruby1.9.3-p545のインストール
  • gemでrbenv-rehash、rails3.2.14のインストール

で、追加したテストケースはこちら。

# package for rails3-tutorial
describe package('sqlite-devel') do
  it { should be_installed }
end

# ruby versions
describe command('ruby -v') do
  it { should return_stdout /ruby 1\.9\.3p545.+/ }
end

# for gem packages
describe package('rbenv-rehash') do
  it { should be_installed.by('gem') }
end

describe package('rails') do
  it { should be_installed.by('gem').with_version('3.2.14') }
end

epel、remiに関しては最初に書いたときに入れていたので割愛します。で、さっそく実行してみると

C:\Users\m-namiki\Documents\20_vagrant\rails3_tutorial>rake spec
C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb
..........FFF

Failures:

  1) Command "ruby -v" should return stdout /ruby 1\.9\.3p545.+/
     Failure/Error: it { should return_stdout /ruby 1\.9\.3p545.+/ }
       sudo ruby -v
       ruby 1.8.7 (2011-06-30 patchlevel 352) [x86_64-linux]
       expected Command "ruby -v" to return stdout /ruby 1\.9\.3p545.+/
     # ./spec/default/base_spec.rb:36:in `block (2 levels) in <top (required)>'

  2) Package "rbenv-rehash" should be installed
     Failure/Error: it { should be_installed.by('gem') }
       sudo gem list --local | grep -w -- \^rbenv-rehash
       expected Package "rbenv-rehash" to be installed
     # ./spec/default/base_spec.rb:41:in `block (2 levels) in <top (required)>'

  3) Package "rails" should be installed
     Failure/Error: it { should be_installed.by('gem').with_version('3.2.14') }
       sudo gem list --local | grep -w -- \^rails | grep -w -- 3.2.14
       expected Package "rails" to be installed
     # ./spec/default/base_spec.rb:45:in `block (2 levels) in <top (required)>'

Finished in 27.6 seconds
13 examples, 3 failures

Failed examples:

rspec ./spec/default/base_spec.rb:36 # Command "ruby -v" should return stdout /r
uby 1\.9\.3p545.+/
rspec ./spec/default/base_spec.rb:41 # Package "rbenv-rehash" should be installe
d
rspec ./spec/default/base_spec.rb:45 # Package "rails" should be installed
C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb failed

残念、失敗!エラーメッセージを見るとsudoしているので、rbenvでインストールしたrubyのバージョンが違ったり、gemで認識されていなかったりするようです。なのでまずはvagrantユーザで実行しているrubygemはどこを見ているのか確認してみます。

[vagrant@localhost ~]$ which ruby
/opt/rbenv/shims/ruby
[vagrant@localhost shims]$ which gem
/opt/rbenv/shims/gem

なるほど、ならばリソースタイプがcommandの場合はこのパスを追加してあげれば良さそうですが、packageの場合はどうしよう。答えは公式ドキュメントに書いてありました。ページ下段の方にあるBlock scoped PATH environment variableを読んでみるとブロック内でPATHを指定することができるようです。なので以下のように書き換えてみました。

# ruby versions
describe command('ruby -v') do
  let(:path) { '/opt/rbenv/shims' }
  it { should return_stdout /ruby 1\.9\.3p545.+/ }
end

# for gem packages
describe package('rbenv-rehash') do
  let(:path) { '/opt/rbenv/shims' }
  it { should be_installed.by('gem') }
end

describe package('rails') do
  let(:path) { '/opt/rbenv/shims' }
  it { should be_installed.by('gem').with_version('3.2.14') }
end

同じ設定を3回書いてるのがちょっとアレですが、これで実行してみます。

C:\Users\m2-namiki\Documents\20_vagrant\rails3_tutorial>rake spec
C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb
...............

Finished in 29.69 seconds
15 examples, 0 failures

今度は無事に成功しました。公式ドキュメントは隅々まで読まなきゃダメですね。。。あとはChefでrbenvを使ってRubyをインストールするCookbookを書いた | ひげろぐを参考にさせていただき、最終的なテストケースはこんな感じになりました。
serverspecをvagrantと連携させるためのプラグインvagrant-serverspec)もあるそうなので、今度はこちらも試してみようと思います。

serverspecを書いてみた

ここら辺を調べてるときにチラチラと見かけたserverspecが気になっていたので調べてみました。serverspec - Homeはサーバの状態・設定をテストするためのプロダクト、のようです。なので、Railsチュートリアル環境に対してのテストを書いてみたいと思います。

serverspecのインストール

serverspecはgemで提供されているので、まずはホームディレクトリにGemfileを作成します。

// ~/Gemfile
source 'https://rubygems.org'
gem "serverspec", "1.0.0"

続いてインストール。

C:\Users\m-namiki>bundle install
Using diff-lcs 1.2.5
Using highline 1.6.21
Using net-ssh 2.8.0
Using rspec-core 2.14.8
Using rspec-expectations 2.14.5
Using rspec-mocks 2.14.6
Using rspec 2.14.1
Using specinfra 1.0.1
Using serverspec 1.0.0
Using bundler 1.6.0
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

無事インストールされたので、Railsチュートリアル環境のディレクトリで初期設定を行います。

C:\Users\m-namiki>cd vagrant\rails3-tutorial
C:\Users\m-namiki\vagrant\rails3-tutorial>serverspec-init
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: y
Auto-configure Vagrant from Vagrantfile? y/n: y
 + spec/
 + spec/default/
 + spec/default/httpd_spec.rb
 + spec/spec_helper.rb
 + Rakefile

テストコードを書く

Rakefileを見るとspec/以下にある*_spec.rbを実行対象とするようです。なので、デフォルトで作られるhttpd_spec.rbは削除して、新たにbase_spec.rbを書きます。

require 'spec_helper'

describe yumrepo('epel') do
  it { should exist }
  it { should be_enabled }
end

describe yumrepo('remi') do
  it { should exist }
  it { should be_enabled }
end

yumリポジトリにepelとremiが存在するかどうかのテストケースを作成してみました。上記でyumrepoと書いてある部分をResourceTypeといい、他にpackageとかserviceとか色々あるようです。詳細はserverspec - Resource Typesに記載されているので、後々確認してみます。

テスト実行

テストケースが完成したのでさっそく実行。

C:\Users\m-namiki\vagrant\rails3-tutorial>rake spec
C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb
Unable to find ~/.rspec because the HOME environment variable is not set
FFFF

Failures:

  1) Yumrepo "epel"
     Failure/Error: Unable to find matching line from backtrace
     ArgumentError: non-absolute home

       non-absolute home

(中略)

Finished in 0.002 seconds
4 examples, 4 failures

Failed examples:

rspec ./spec/default/base_spec.rb:4 # Yumrepo "epel"
rspec ./spec/default/base_spec.rb:5 # Yumrepo "epel"
rspec ./spec/default/base_spec.rb:9 # Yumrepo "remi"
rspec ./spec/default/base_spec.rb:10 # Yumrepo "remi"
C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb failed

見事に失敗。メッセージを読むと環境変数HOMEがないから.rspecが見つからないよと言われてるようです。なので確認してみます。

C:\Users\m-namiki\vagrant\rails3-tutorial>set HOME
HOME=%USERPROFILE%
HOMEDRIVE=C:
HOMEPATH=\Users\m-namiki
C:\Users\m-namiki\vagrant\rails3-tutorial>set USERPROFILE
USERPROFILE=C:\Users\m-namiki

環境変数HOMEは設定されていますが、Windows的な何かじゃないのかもしれない。。。なのでRakefileに定義してみました。

require 'rake'
require 'rspec/core/rake_task'

# ここにHOMEを追記
ENV['HOME'] = 'C:\Users\m-namiki'

RSpec::Core::RakeTask.new(:spec) do |t|
  t.pattern = 'spec/*/*_spec.rb'
end

task :default => :spec

で、改めて実行してみると

C:\Users\m-namiki\vagrant\rails3-tutorial>rake spec
C:/Ruby193/bin/ruby.exe -S rspec spec/default/base_spec.rb
....

Finished in 25.05 seconds
4 examples, 0 failures

今度は無事に成功しました。まだ簡単なテストしか書いていないので次はもうちょっと色々なケースを追加してみようと思います。

heroku toolbeltのインストールでハマった話

Ruby on Rails チュートリアル:実例を使って Rails を学ぼうの1.4節でHerokuにデプロイするというのがありますが、色々ハマったのでメモしておきます。

CentOS6.5でHeroku Toolbeltからtoolbetをダウンロードする場合、Standaloneを選択して下に表示されたwget -qO- https://toolbelt.heroku.com/install.sh | shを実行すれば良いはず。なので、まずはその通りにやってみました。

[vagrant@localhost ~]$ wget -qO- https://toolbelt.heroku.com/install.sh | sh
[vagrant@localhost ~]$ heroku login
-bash: heroku: command not found

が、インストールされていない。。。ので、「CentOSにheroku toolbeltをインストール」が失敗する場合の対処を参考に、証明書を無視する形で実行してみました。

[vagrant@localhost ~]$ wget --no-check-certificate https://toolbelt.heroku.com/install.sh
--2014-03-27 03:06:26--  https://toolbelt.heroku.com/install.sh
Resolving toolbelt.heroku.com... 23.21.54.126, 23.23.227.87, 50.19.233.212
Connecting to toolbelt.heroku.com|23.21.54.126|:443... connected.
WARNING: certificate common name www.heroku.comtoolbelt.heroku.cominstall.shinstall.sh
[vagrant@localhost ~]$ sudo sh ./install.sh 
This script requires superuser access to install software.
You will be prompted for your password by sudo.
Add the Heroku CLI to your PATH using:
$ echo 'PATH="/usr/local/heroku/bin:$PATH"' >> ~/.profile
Installation complete
[vagrant@localhost ~]$ echo 'PATH="/usr/local/heroku/bin:$PATH"' >> .bashrc
[vagrant@localhost ~]$ cat .bashrc
# .bashrc

# Source global definitions
if [ -f /etc/bashrc ]; then
        . /etc/bashrc
fi

# User specific aliases and functions
PATH="/usr/local/heroku/bin:$PATH"
[vagrant@localhost ~]$ source .bashrc

で、実行してみると

[vagrant@localhost ~]$ heroku version
/opt/rbenv/versions/1.9.3-p545/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- heroku-api (LoadError)
        from /opt/rbenv/versions/1.9.3-p545/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /usr/local/heroku/lib/heroku/client/organizations.rb:1:in `<top (required)>'
        from /opt/rbenv/versions/1.9.3-p545/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /opt/rbenv/versions/1.9.3-p545/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /usr/local/heroku/lib/heroku/command/base.rb:4:in `<top (required)>'
        from /opt/rbenv/versions/1.9.3-p545/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /opt/rbenv/versions/1.9.3-p545/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /usr/local/heroku/lib/heroku/command/auth.rb:1:in `<top (required)>'
        from /opt/rbenv/versions/1.9.3-p545/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /opt/rbenv/versions/1.9.3-p545/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
        from /usr/local/heroku/lib/heroku/command.rb:14:in `block in load'
        from /usr/local/heroku/lib/heroku/command.rb:13:in `each'
        from /usr/local/heroku/lib/heroku/command.rb:13:in `load'
        from /usr/local/heroku/lib/heroku/cli.rb:27:in `start'
        from /usr/local/heroku/bin/heroku:24:in `<main>'

heroku-apiというのが足りないらしい。これまた調べてみるとgemで提供されているようなのでGemfileを作ってbundlerでインストールしておきます。

[vagrant@localhost ~]$ cat Gemfile
source 'https://rubygems.org'
gem "heroku-api", "0.3.18"
[vagrant@localhost ~]$ bundle install
Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Installing excon (0.32.1)
Using multi_json (1.9.2)
Installing heroku-api (0.3.18)
Using bundler (1.5.3)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.

で、確認。

[vagrant@localhost ~]$ heroku version
heroku-toolbelt/3.6.0 (x86_64-linux) ruby/1.9.3

今度は無事に実行できるようになりました。

データベース徹底攻略とWebアプリエンジニア養成読本

データベース徹底攻略 (WEB+DB PRESS plus)

データベース徹底攻略 (WEB+DB PRESS plus)

Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)

Webアプリエンジニア養成読本[しくみ、開発、環境構築・運用…全体像を最新知識で最初から! ] (Software Design plus)

最近立て続けに楽しい本を読めて嬉しい限り。

Ruby on Railsチュートリアルの写経を始めました

Ruby on Rails チュートリアル:実例を使って Rails を学ぼう
Vagrant+Chefで環境構築する方法がなんとなく理解できてきましたが、ただ環境を作るだけじゃもったいないのでRailsチュートリアルの写経を始めてみようと思います。環境は以下の通りです。

OS Windows7 x64
VirtualBox VirtualBox 4.3.8 for Windows hosts
Ruby ruby 1.9.3p484 (2013-11-22) [i386-mingw32]
Vagrant Vagrant 1.5.1
chef chef (11.10.4 x86-mingw32)
knife knife-solo (0.4.1)
berkshelf berkshelf (2.0.14)

VagrantとChefの導入についてはVagrant + Chefの導入 vol.1 - m-namikiの日記Vagrant + Chefの導入 vol.2 - m-namikiの日記で行ってますが、vagrant用のプラグインとberkshelfのインストールをしてなかったのでしておきます。

saharaのインストール

saharaは仮想サーバのスナップショットのようなものを作ることができるvagrantプラグインです。環境のロールバック、コミットが可能なので何かあった場合に簡単に変更を取り消すことができます。インストール方法は以下の通りです。

vagrant plugin install sahara

vagrant-omnibusのインストール

vagrant-omnibusは仮想サーバ起動時にChefがインストールされているかどうかをチェックして、インストールされていない場合は自動でインストールしてくれるvagrantプラグインです。環境構築を自動化したいのにそのために手動でChefをインストールするのは本末転倒なので非常にありがたいですね。インストール方法は以下の通りです。

vagrant plugin install vagrant-omnibus

Berkshelfのインストール

BerkshelfはChefのはcookbookの依存関係を管理するツール(らしい)です。まだhttp://community.opscode.com/からcookbookをダウンロードするのが楽になるくらいしか分かっていませんが、インストールしておきます。gemとして提供されているので以下の通りにインストールします。

gem install berkshelf --no-ri --no-rdoc

仮想サーバの作成

ここから仮想サーバの作成を行っていきます。

cd C:\Users\m-namiki\Documents\20_vagrant
mkdir rails3_tutorial
cd rails3_tutorial
vagrant init chef/centos-6.5

chef-repoの作成

Vagrantfileがある場所にchef用のリポジトリを作成します。

knife solo init chef-repo
cd chef-repo
knife cookbook create ruby_env -o site-cookbooks

Berksfileの編集

Berkshelfがインストールしてあるとknife cookbook時にBerksfileが作成されるので編集します。

// chef-repo/Berksfile
site :opscode
cookbook 'yum'
cookbook 'ruby_build'
cookbook 'rbenv'
cookbook 'nodejs'

rubyのインストールはrbenv+ruby_buildで行います。nodejsについては、rails serverを実行したときにJavaScriptランタイムがインストールされていないと言われたのでインストールします。で、Berkshelfの実行。

berks install --path cookbooks

これでcookbooks/以下にyumruby_build、rbenv、その他それらと依存関係があるcookbookがインストールされました。

site-cookbooks/ruby_env/recipes/default.rbの編集

続いてレシピの編集です。

# for install Ruby
include_recipe "rbenv::default"
include_recipe "rbenv::ruby_build"
include_recipe "rbenv::rbenv_vars"

service "iptables" do
  action [:disable, :stop]
end
log "done iptables stop."

# add the EPEL repo
yum_repository 'epel' do
  description 'Extra Packages for Enterprise Linux'
  mirrorlist 'http://mirrors.fedoraproject.org/mirrorlist?repo=epel-6&arch=$basearch'
  fastestmirror_enabled true
  gpgkey 'http://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-6'
  action :create
end

# add the Remi repo
yum_repository 'remi' do
  description 'Les RPM de Remi - Repository'
  baseurl 'http://rpms.famillecollet.com/enterprise/6/remi/x86_64/'
  gpgkey 'http://rpms.famillecollet.com/RPM-GPG-KEY-remi'
  fastestmirror_enabled true
  action :create
end

%w{sqlite-devel}.each do |p|
  package p do
    action :install
  end
end
log "done package install."

rbenv_ruby "1.9.3-p545" do
  ruby_version "1.9.3-p545"
  global true
end
log "done ruby install."

gem_package "rbenv-rehash" do
  action :install
end
log "done rehash install."

gem_package "rails" do
  action :install
  version '3.2.14'
end
log "done rails3 install."

rbenvでrubyをインストールした場合、gem install後に必ずrbenv rehashをしなければならないそうなので、それを回避するためにrbenv-rehashを一緒にインストールしておきます。

Vagrantfileを編集

最後にVagrantfileを編集します。以下は変更部分です。

# Railsのサーバ用ポート
config.vm.network "forwarded_port", guest: 3000, host: 3333

config.vm.network "private_network", ip: "192.168.33.10"
config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--memory", "1024"]
end

config.vm.provision "chef_solo" do |chef|
  chef.cookbooks_path = ["./chef-repo/cookbooks", "./chef-repo/site-cookbooks"]
  chef.add_recipe "yum"
  chef.add_recipe "rbenv"
  chef.add_recipe "ruby_env"
  chef.add_recipe "nodejs"
end

# この行追加
config.omnibus.chef_version = :latest

forwarded_portについては、rails server実行時にhttp://localhost:3000にアクセスしろと言われますが、そのままではアクセスできなかったので3333に変更しました。

起動

準備が整ったのでvagrant upで起動します。自分の環境だとrubyとnodejsのインストールにそれぞれ10分くらい掛かりましたが、無事インストールが完了して起動しました。

[vagrant@localhost ~]$ ruby -v
ruby 1.9.3p545 (2014-02-24 revision 45159) [x86_64-linux]
[vagrant@localhost ~]$ rails -v
Rails 3.2.14
[vagrant@localhost ~]$ node -v
v0.10.15

で、ここまでの状態を一旦コミットしておきます。

vagrant sandbox on
vagrant sandbox commit

これで何か変更を加えてもすぐに元に戻せるので安心です。

作成したVagrantfileやchef-repoを以下で公開してみました。
m-namiki/rails3-tutorial_environment · GitHub
写経したものはこっちに公開していきます。
m-namiki/rails3-projects · GitHub
実はこれをやってみたかった:)

GitHub実践入門

GitHub実践入門 ~Pull Requestによる開発の変革 (WEB+DB PRESS plus)

GitHub実践入門 ~Pull Requestによる開発の変革 (WEB+DB PRESS plus)

昨日夜受け取ったのでまだちょっと少ししか読めてませんが、ワクワクしながら読んでます。これに限らず最近買った書籍はみんな読むのが楽しいです。

Vagrant + Chefの導入 vol.2

入門Chef Solo - Infrastructure as Code

入門Chef Solo - Infrastructure as Code

前回の記事ではVagrantの導入までが終わったので、今回はChefを導入してみます。

Rubyのインストール

ChefはRubyで動作するプログラムなので、Rubyをインストールしておく必要があります。Windows環境へのインストール方法はこちらを参照してください。バージョンについては、自分は1.9.3-p484を使っていますが、最新は1.9.3-p545なのでそちらの方が良いかと思います。
また、DevKitというものも必要となりますので、そちらもインストールしておきます。インストール方法はこちらを参照してください。

Chefのインストール

続いてはChefのインストールですが、まずは周辺プロダクトを整理してみます。

プロダクト名 概要
chef-solo chefに同梱されている単体で動作するプロビジョニングツール*1
knife-solo chefのcookbookを作るためのツール
Berkshelf chefで使うcookbookを管理するgem

Chefはクライアント・サーバによる集中管理方式でサーバの構成管理を行えますが、スタンドアロン方式も可能です。chef-soloはそのスタンドアロン方式用のツールです。次にknife-soloはknifeのプラグインでchef-soloで利用するレシピの雛形を作成したり、リモートでレシピを実行できるようにするためのサブコマンドが使えるようになります。Berkshelfはcookbookの依存関係を管理するツールらしいですが、ちょっとピンときません。ここは追々調べてみようと思います。

コマンドプロンプトで以下のコマンドを実行します。

gem install chef
gem install knife-solo

cookbookの作成

インストールが完了したら、vagrant initしたディレクトリに移動してknife soloを実行します。前回の記事の例だとC:\Users\m-namiki\Documents\20_vagrant\10_sandboxになります。

cd C:\Users\m-namiki\Documents\20_vagrant\10_sandbox
knife solo init chef-repo

実行が完了するとchef-repoというディレクトリが作成され、その下の階層は以下のようになります。

C:\Users\m-namiki\Documents\20_vagrant\10_sandbox>tree chef-repo
C:\USERS\M-NAMIKI\DOCUMENTS\20_VAGRANT\10_SANDBOX\CHEF-REPO
├─.chef
├─cookbooks
├─data_bags
├─environments
├─nodes
├─roles
└─site-cookbooks

次に簡単なレシピを書いてみます。chef-repoに移動して、knife cookbookを実行します。

cd chef-repo
knife cookbook create sandbox -o site-cookbooks

実行が完了するとsite-cookbooks以下にsandboxというディレクトリが作成され、その中にも色々なディレクトリやファイルが作成されます。

C:\Users\m-namiki\Documents\20_vagrant\10_sandbox\chef-repo>tree site-cookbooks
C:\USERS\M-NAMIKI\DOCUMENTS\20_VAGRANT\10_sandbox\CHEF-REPO\SITE-COOKBOOKS
└─sandbox
    ├─attributes
    ├─definitions
    ├─files
    │  └─default
    ├─libraries
    ├─providers
    ├─recipes
    ├─resources
    └─templates
        └─default

recipes\default.rbというファイルがインストールするパッケージなどを記述するファイルです。まずは簡単にiptablesを停止・無効化するレシピを書いてみます。

#
# Cookbook Name:: sandbox
# Recipe:: default
#
# Copyright 2014, YOUR_COMPANY_NAME
#
# All rights reserved - Do Not Redistribute
#

service "iptables" do
  action [:disable, :stop]
end

log "done iptables stop."

レシピの書き方等については伊藤直也さん(@naoya)の入門Chef Solo - Infrastructure as Codeを筆頭に、ググればたくさん出てくるのでここでは割愛します。そしてようやく次は実行ですが、残念、m-namikiは力尽きてしまった!ので、続きはまた今度。

*1:サーバー構築・管理を自動化するツール